1. useReducer 基本概念
useReducer 是 React 的一个 Hook,用于管理复杂的状态逻辑。它接收一个 reducer 函数和初始状态,返回当前状态和 dispatch 函数。
1.1 基本语法
const [state, dispatch] = useReducer(reducer, initialState, init);
- reducer: (state, action) => newState
- initialState: 初始状态
- init: (可选) 惰性初始化函数
2. 基础示例
2.1 简单计数器
function counterReducer(state, action) {switch (action.type) {case 'increment':return { count: state.count + 1 };case 'decrement':return { count: state.count - 1 };default:return state;}
}function Counter() {const [state, dispatch] = useReducer(counterReducer, { count: 0 });return (<div>Count: {state.count}<button onClick={() => dispatch({ type: 'increment' })}>+</button><button onClick={() => dispatch({ type: 'decrement' })}>-</button></div>);
}
3. 复杂状态管理示例
3.1 待办事项列表
const todoReducer = (state, action) => {switch (action.type) {case 'ADD_TODO':return {...state,todos: [...state.todos, {id: Date.now(),text: action.payload,completed: false}]};case 'TOGGLE_TODO':return {...state,todos: state.todos.map(todo =>todo.id === action.payload? { ...todo, completed: !todo.completed }: todo)};case 'DELETE_TODO':return {...state,todos: state.todos.filter(todo => todo.id !== action.payload)};default:return state;}
};function TodoApp() {const [state, dispatch] = useReducer(todoReducer, { todos: [] });const [input, setInput] = useState('');const handleSubmit = (e) => {e.preventDefault();if (!input.trim()) return;dispatch({ type: 'ADD_TODO', payload: input });setInput('');};return (<div><form onSubmit={handleSubmit}><inputvalue={input}onChange={e => setInput(e.target.value)}placeholder="添加待办事项"/><button type="submit">添加</button></form><ul>{state.todos.map(todo => (<li key={todo.id}><spanstyle={{textDecoration: todo.completed ? 'line-through' : 'none'}}onClick={() => dispatch({type: 'TOGGLE_TODO',payload: todo.id})}>{todo.text}</span><button onClick={() => dispatch({type: 'DELETE_TODO',payload: todo.id})}>删除</button></li>))}</ul></div>);
}
3.2 表单状态管理
const formReducer = (state, action) => {switch (action.type) {case 'SET_FIELD':return {...state,[action.field]: action.value};case 'SET_ERROR':return {...state,errors: {...state.errors,[action.field]: action.error}};case 'RESET_FORM':return initialState;default:return state;}
};function ComplexForm() {const initialState = {username: '',email: '',password: '',errors: {}};const [state, dispatch] = useReducer(formReducer, initialState);const handleChange = (e) => {const { name, value } = e.target;dispatch({type: 'SET_FIELD',field: name,value});// 验证逻辑if (name === 'email' && !value.includes('@')) {dispatch({type: 'SET_ERROR',field: 'email',error: '请输入有效的邮箱地址'});}};return (<form><div><inputname="username"value={state.username}onChange={handleChange}placeholder="用户名"/></div><div><inputname="email"value={state.email}onChange={handleChange}placeholder="邮箱"/>{state.errors.email && (<span style={{ color: 'red' }}>{state.errors.email}</span>)}</div><div><inputname="password"type="password"value={state.password}onChange={handleChange}placeholder="密码"/></div><button type="button" onClick={() => dispatch({ type: 'RESET_FORM' })}>重置</button></form>);
}
4. 使用 Immer 简化 Reducer 逻辑
Immer 允许我们以更直观的方式编写 reducer,无需手动处理不可变性。
4.1 安装 Immer
npm install immer
4.2 使用 Immer 重写 Todo 示例
import produce from 'immer';const todoReducer = produce((draft, action) => {switch (action.type) {case 'ADD_TODO':draft.todos.push({id: Date.now(),text: action.payload,completed: false});break;case 'TOGGLE_TODO':const todo = draft.todos.find(t => t.id === action.payload);if (todo) {todo.completed = !todo.completed;}break;case 'DELETE_TODO':const index = draft.todos.findIndex(t => t.id === action.payload);if (index !== -1) {draft.todos.splice(index, 1);}break;}
});
4.3 使用 Immer 简化复杂状态更新
具体参照:[https://immerjs.github.io/immer/zh-CN/example-setstate]
const complexReducer = produce((draft, action) => {switch (action.type) {case 'UPDATE_NESTED_STATE':draft.users[action.userId].preferences.theme = action.theme;break;case 'ADD_ITEM_TO_ARRAY':draft.items[action.categoryId].push(action.item);break;case 'UPDATE_MULTIPLE_FIELDS':Object.assign(draft.form, action.updates);break;}
});function ComplexStateComponent() {const [state, dispatch] = useReducer(complexReducer, {users: {},items: {},form: {}});// 使用示例const updateTheme = (userId, theme) => {dispatch({type: 'UPDATE_NESTED_STATE',userId,theme});};const addItem = (categoryId, item) => {dispatch({type: 'ADD_ITEM_TO_ARRAY',categoryId,item});};
}
5. useReducer 使用场景
- 复杂的状态逻辑:当组件状态逻辑复杂,包含多个值时
- 相关状态更新:当多个状态更新紧密相关时
- 状态依赖其他状态:当状态更新依赖于其他状态值时
- 深层状态更新:当需要更新深层嵌套的状态时
- 状态更新需要集中管理:当需要在一个地方管理所有状态更新逻辑时
6. 最佳实践
- Action 类型常量化
const TODO_ACTIONS = {ADD: 'ADD_TODO',TOGGLE: 'TOGGLE_TODO',DELETE: 'DELETE_TODO'
};
- Action Creator 函数化
const createTodo = (text) => ({type: TODO_ACTIONS.ADD,payload: text
});
- 使用 TypeScript 定义类型
interface Todo {id: number;text: string;completed: boolean;
}type TodoAction = | { type: 'ADD_TODO'; payload: string }| { type: 'TOGGLE_TODO'; payload: number }| { type: 'DELETE_TODO'; payload: number };const todoReducer = (state: Todo[], action: TodoAction): Todo[] => {// reducer 逻辑
};
- 合理拆分 Reducer
const rootReducer = (state, action) => {return {todos: todosReducer(state.todos, action),user: userReducer(state.user, action),ui: uiReducer(state.ui, action)};
};
通过使用 useReducer 和 Immer,我们可以更好地管理复杂的状态逻辑,同时保持代码的可读性和可维护性。Immer 特别适合处理深层嵌套的状态更新,让代码更简洁直观。