Redux 扩展与标准化模板方案

Redux 扩展与标准化模板方案

一、Redux 标准化架构设计

1. 目录结构规范

src/
  redux/
    ├── store.js           # Store配置入口
    ├── rootReducer.js     # 根Reducer组合
    ├── rootSaga.js        # 根Saga组合(如使用redux-saga)
    ├── modules/           # 功能模块
    │   ├── user/          # 用户模块
    │   │   ├── actions.js
    │   │   ├── reducer.js
    │   │   ├── types.js
    │   │   └── saga.js    # 可选
    │   └── product/       # 产品模块
    ├── middleware/        # 自定义中间件
    └── utils/             # 工具函数

2. 类型常量模板 (types.js)

// 使用常量前缀避免命名冲突
export const USER_LOGIN_REQUEST = 'USER/LOGIN_REQUEST';
export const USER_LOGIN_SUCCESS = 'USER/LOGIN_SUCCESS';
export const USER_LOGIN_FAILURE = 'USER/LOGIN_FAILURE';

// 使用对象整合便于引用
export const userTypes = {
  LOGIN_REQUEST: 'USER/LOGIN_REQUEST',
  LOGIN_SUCCESS: 'USER/LOGIN_SUCCESS',
  LOGIN_FAILURE: 'USER/LOGIN_FAILURE'
};

3. Action 创建函数模板 (actions.js)

import * as types from './types';

// 基础action创建函数
export const loginRequest = (credentials) => ({
  type: types.USER_LOGIN_REQUEST,
  payload: credentials
});

// 使用redux-thunk的异步action
export const loginUser = (credentials) => async (dispatch) => {
  dispatch(loginRequest(credentials));
  try {
    const response = await api.login(credentials);
    dispatch({ type: types.USER_LOGIN_SUCCESS, payload: response.data });
    return response;
  } catch (error) {
    dispatch({ type: types.USER_LOGIN_FAILURE, error: error.message });
    throw error;
  }
};

// 使用redux-saga的action (只需定义普通action)
export const fetchUserProfile = (userId) => ({
  type: types.USER_FETCH_PROFILE,
  payload: { userId }
});

4. Reducer 模板 (reducer.js)

import * as types from './types';

const initialState = {
  loading: false,
  data: null,
  error: null
};

export default function userReducer(state = initialState, action) {
  switch (action.type) {
    case types.USER_LOGIN_REQUEST:
      return { ...state, loading: true, error: null };
    
    case types.USER_LOGIN_SUCCESS:
      return { ...state, loading: false, data: action.payload };
    
    case types.USER_LOGIN_FAILURE:
      return { ...state, loading: false, error: action.error };
    
    default:
      return state;
  }
}

二、Redux 高级扩展方案

1. 动态注入Reducer

// store.js
import { createStore, combineReducers, applyMiddleware } from 'redux';

export function configureStore() {
  const store = createStore(
    createReducer(),
    applyMiddleware(...middlewares)
  );

  // 异步reducer注入
  store.asyncReducers = {};
  store.injectReducer = (key, asyncReducer) => {
    store.asyncReducers[key] = asyncReducer;
    store.replaceReducer(createReducer(store.asyncReducers));
  };

  return store;
}

function createReducer(asyncReducers) {
  return combineReducers({
    ...staticReducers,
    ...asyncReducers
  });
}

2. 标准化中间件配置

// middleware/index.js
import { createLogger } from 'redux-logger';
import thunk from 'redux-thunk';
import errorMiddleware from './errorMiddleware';

const isProduction = process.env.NODE_ENV === 'production';

export const getMiddlewares = () => {
  const middlewares = [thunk, errorMiddleware];
  
  if (!isProduction) {
    middlewares.push(createLogger({
      collapsed: true,
      duration: true,
      diff: true
    }));
  }
  
  return middlewares;
};

// 错误处理中间件示例
export const errorMiddleware = store => next => action => {
  try {
    return next(action);
  } catch (err) {
    console.error('Redux error:', err);
    store.dispatch({ type: 'REDUX_ERROR', error: err });
    throw err;
  }
};

3. 状态选择器模板 (selectors.js)

// 基础选择器
export const getUserState = (state) => state.user;

// 记忆化选择器(使用reselect)
import { createSelector } from 'reselect';

export const getCurrentUser = createSelector(
  [getUserState],
  (user) => user.data
);

export const getAuthStatus = createSelector(
  [getUserState],
  (user) => ({
    isAuthenticated: !!user.data,
    isLoading: user.loading
  })
);

三、Redux 工具集扩展

1. Redux Toolkit 整合

// 使用@reduxjs/toolkit简化代码
import { createSlice, configureStore } from '@reduxjs/toolkit';

const userSlice = createSlice({
  name: 'user',
  initialState: { loading: false, data: null },
  reducers: {
    loginRequest: (state) => {
      state.loading = true;
    },
    loginSuccess: (state, action) => {
      state.loading = false;
      state.data = action.payload;
    },
    loginFailure: (state, action) => {
      state.loading = false;
      state.error = action.payload;
    }
  }
});

export const { actions, reducer } = userSlice;

2. 持久化存储方案

// 使用redux-persist
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';

const persistConfig = {
  key: 'root',
  storage,
  whitelist: ['user'], // 只持久化user模块
  transforms: [/* 自定义转换器 */]
};

const persistedReducer = persistReducer(
  persistConfig,
  rootReducer
);

export const store = configureStore({ reducer: persistedReducer });
export const persistor = persistStore(store);

3. 类型安全增强 (TypeScript)

// 类型化Redux
import { Action } from 'redux';
import { ThunkAction } from 'redux-thunk';

type UserState = {
  data: User | null;
  loading: boolean;
  error: string | null;
};

type AppThunk<ReturnType = void> = ThunkAction<
  ReturnType,
  RootState,
  unknown,
  Action<string>
>;

// 类型化action
interface LoginSuccessAction {
  type: typeof USER_LOGIN_SUCCESS;
  payload: User;
}

type UserActionTypes = LoginRequestAction | LoginSuccessAction | LoginFailureAction;

// 类型化reducer
export function userReducer(
  state: UserState = initialState,
  action: UserActionTypes
): UserState {
  // ...实现
}

四、最佳实践建议

1. 性能优化策略

  • 批量更新:使用redux-batched-actions减少渲染次数
  • 记忆化选择器:避免不必要的重新计算
  • 按需加载:动态注入reducer和saga

2. 测试方案

// reducer测试示例
describe('user reducer', () => {
  it('should handle LOGIN_REQUEST', () => {
    expect(
      userReducer(initialState, { type: types.USER_LOGIN_REQUEST })
    ).toEqual({
      loading: true,
      data: null,
      error: null
    });
  });
});

// action测试示例
describe('loginUser', () => {
  it('dispatches success on successful API call', async () => {
    const mockUser = { id: 1, name: 'Test User' };
    api.login = jest.fn().mockResolvedValue({ data: mockUser });
    
    const dispatch = jest.fn();
    await loginUser({ email, password })(dispatch);
    
    expect(dispatch).toHaveBeenCalledWith(
      expect.objectContaining({
        type: types.USER_LOGIN_SUCCESS,
        payload: mockUser
      })
    );
  });
});

3. 开发调试建议

  • Redux DevTools:集成浏览器插件
  • Action追踪:为每个action添加唯一ID
  • 状态快照:实现状态历史记录功能

通过以上扩展方案,可以构建出结构清晰、易于维护且功能强大的Redux架构,适用于各种规模的前端应用开发。根据项目需求,可以选择性地采用这些方案进行组合使用。

你可能感兴趣的:(Redux 扩展与标准化模板方案)