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/ # 工具函数
// 使用常量前缀避免命名冲突
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'
};
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 }
});
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;
}
}
// 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
});
}
// 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;
}
};
// 基础选择器
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
})
);
// 使用@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;
// 使用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);
// 类型化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 {
// ...实现
}
redux-batched-actions
减少渲染次数// 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
})
);
});
});
通过以上扩展方案,可以构建出结构清晰、易于维护且功能强大的Redux架构,适用于各种规模的前端应用开发。根据项目需求,可以选择性地采用这些方案进行组合使用。