React15 - 在ruducer中以对象映射替代switch语句
完全可以使用对象映射来代替 Redux reducer 中的 switch case 语句, 这种写法更加简洁、易维护,也符合函数式编程的思想。以下是几种实现方式:
1. 基础对象映射方式
// 定义 action 类型常量
const TYPES = {
ADD_TODO: 'ADD_TODO',
UPDATE_TODO: 'UPDATE_TODO',
DELETE_TODO: 'DELETE_TODO',
TOGGLE_TODO: 'TOGGLE_TODO'
};
// 创建 action 处理函数的映射对象
const handlers = {
[TYPES.ADD_TODO]: (state, action) => ({
...state,
todos: [...state.todos, action.payload]
}),
[TYPES.UPDATE_TODO]: (state, action) => ({
...state,
todos: state.todos.map(todo =>
todo.id === action.payload.id ? action.payload : todo
)
}),
[TYPES.DELETE_TODO]: (state, action) => ({
...state,
todos: state.todos.filter(todo => todo.id !== action.payload)
}),
[TYPES.TOGGLE_TODO]: (state, action) => ({
...state,
todos: state.todos.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
)
})
};
// reducer 使用映射对象
const todoReducer = (state = initialState, action) => {
const handler = handlers[action.type];
return handler ? handler(state, action) : state;
};
2. 封装成通用函数
// 创建一个通用的 createReducer 函数
const createReducer = (initialState, handlers) => {
return (state = initialState, action) => {
const handler = handlers[action.type];
return handler ? handler(state, action) : state;
};
};
// 使用示例
const todoReducer = createReducer(initialState, {
[TYPES.ADD_TODO]: (state, action) => ({
...state,
todos: [...state.todos, action.payload]
}),
[TYPES.UPDATE_TODO]: (state, action) => ({
...state,
todos: state.todos.map(todo =>
todo.id === action.payload.id ? action.payload : todo
)
})
});
3. 使用 Redux Toolkit(推荐)
Redux Toolkit 内部已经使用了这种模式:
import { createSlice } from '@reduxjs/toolkit';
const todosSlice = createSlice({
name: 'todos',
initialState: {
todos: []
},
reducers: {
addTodo: (state, action) => {
state.todos.push(action.payload);
},
updateTodo: (state, action) => {
const index = state.todos.findIndex(todo => todo.id === action.payload.id);
if (index !== -1) {
state.todos[index] = action.payload;
}
},
deleteTodo: (state, action) => {
state.todos = state.todos.filter(todo => todo.id !== action.payload);
},
toggleTodo: (state, action) => {
const todo = state.todos.find(todo => todo.id === action.payload);
if (todo) {
todo.completed = !todo.completed;
}
}
}
});
// 自动生成的 action creators
export const { addTodo, updateTodo, deleteTodo, toggleTodo } = todosSlice.actions;
export default todosSlice.reducer;
4. 高级封装 - 支持异步和嵌套
const createReducer = (initialState, handlers) => {
// 支持嵌套的 action 类型
const flattenHandlers = (obj, prefix = '') => {
return Object.keys(obj).reduce((acc, key) => {
const fullKey = prefix ? `${prefix}/${key}` : key;
if (typeof obj[key] === 'function') {
acc[fullKey] = obj[key];
} else if (typeof obj[key] === 'object') {
Object.assign(acc, flattenHandlers(obj[key], fullKey));
}
return acc;
}, {});
};
const flatHandlers = flattenHandlers(handlers);
return (state = initialState, action) => {
// 处理普通 action
if (flatHandlers[action.type]) {
return flatHandlers[action.type](state, action);
}
// 处理异步 action 的三个状态
const [baseType, status] = action.type.split('/');
if (status && flatHandlers[`${baseType}/${status}`]) {
return flatHandlers[`${baseType}/${status}`](state, action);
}
return state;
};
};
// 使用示例
const userReducer = createReducer(
{ data: null, loading: false, error: null },
{
FETCH_USER: {
PENDING: (state) => ({ ...state, loading: true, error: null }),
FULFILLED: (state, action) => ({
...state,
loading: false,
data: action.payload
}),
REJECTED: (state, action) => ({
...state,
loading: false,
error: action.payload
})
},
UPDATE_USER: (state, action) => ({
...state,
data: { ...state.data, ...action.payload }
})
}
);
5. TypeScript 版本
type ReducerHandlers<S, A extends Action = AnyAction> = {
[key: string]: (state: S, action: A) => S;
};
function createReducer<S>(
initialState: S,
handlers: ReducerHandlers<S>
): (state: S | undefined, action: AnyAction) => S {
return (state: S = initialState, action: AnyAction): S => {
const handler = handlers[action.type];
return handler ? handler(state, action) : state;
};
}
// 使用示例
interface Todo {
id: string;
text: string;
completed: boolean;
}
interface TodoState {
todos: Todo[];
}
const todoReducer = createReducer<TodoState>(
{ todos: [] },
{
[TYPES.ADD_TODO]: (state, action: PayloadAction<Todo>) => ({
...state,
todos: [...state.todos, action.payload]
}),
[TYPES.TOGGLE_TODO]: (state, action: PayloadAction<string>) => ({
...state,
todos: state.todos.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
)
})
}
);
优点
- 更清晰的代码结构:每个 action 类型对应一个处理函数
- 易于测试:可以单独测试每个 handler 函数
- 易于扩展:添加新的 action 类型只需添加新的键值对
- 类型安全:在 TypeScript 中可以获得更好的类型推断
- 性能更好:对象属性查找比 switch 语句更快
注意事项
- 确保处理了未知的 action 类型(返回原状态)
- 保持 reducer 的纯函数特性
- 不要直接修改 state(除非使用 Redux Toolkit 的 Immer)
这种模式在实际开发中非常实用,特别是配合 Redux Toolkit,可以让代码更加简洁和可维护。

浙公网安备 33010602011771号