
useReducer 与复杂状态一、useReducer 基础1.1 什么是 useReduceruseReducer 是 useState 的替代方案适用于管理复杂的状态逻辑。它类似于 Redux 的 reducer 模式。1.2 基本语法const [state, dispatch] useReducer(reducer, initialState, init);state当前状态dispatch触发状态更新的函数reducer纯函数根据 action 返回新状态initialState初始状态init惰性初始化函数可选1.3 最简单的例子// 定义 reducer function counterReducer(state, action) { switch (action.type) { case increment: return { count: state.count 1 }; case decrement: return { count: state.count - 1 }; case reset: return { count: 0 }; default: return state; } } function Counter() { const [state, dispatch] useReducer(counterReducer, { count: 0 }); return ( div p计数: {state.count}/p button onClick{() dispatch({ type: increment })}/button button onClick{() dispatch({ type: decrement })}-/button button onClick{() dispatch({ type: reset })}重置/button /div ); }二、Reducer 模式详解2.1 Action 的结构// Action 通常包含 type 和 payload const action { type: ADD_TODO, payload: { id: 1, text: 学习 React, completed: false } }; // 或者更简洁的写法 dispatch({ type: ADD_TODO, text: 学习 React, id: 1 }); // Action 类型使用常量 const ACTIONS { ADD_TODO: ADD_TODO, TOGGLE_TODO: TOGGLE_TODO, DELETE_TODO: DELETE_TODO }; dispatch({ type: ACTIONS.ADD_TODO, payload: newTodo });2.2 完整的 Todo 示例// 定义 Action 类型 const ACTIONS { ADD_TODO: ADD_TODO, TOGGLE_TODO: TOGGLE_TODO, DELETE_TODO: DELETE_TODO, EDIT_TODO: EDIT_TODO, CLEAR_COMPLETED: CLEAR_COMPLETED }; // Reducer 函数 function todoReducer(state, action) { switch (action.type) { case ACTIONS.ADD_TODO: return { ...state, todos: [...state.todos, action.payload] }; case ACTIONS.TOGGLE_TODO: return { ...state, todos: state.todos.map(todo todo.id action.payload ? { ...todo, completed: !todo.completed } : todo ) }; case ACTIONS.DELETE_TODO: return { ...state, todos: state.todos.filter(todo todo.id ! action.payload) }; case ACTIONS.EDIT_TODO: return { ...state, todos: state.todos.map(todo todo.id action.payload.id ? { ...todo, text: action.payload.text } : todo ) }; case ACTIONS.CLEAR_COMPLETED: return { ...state, todos: state.todos.filter(todo !todo.completed) }; default: return state; } } // 组件 function TodoApp() { const [state, dispatch] useReducer(todoReducer, { todos: [ { id: 1, text: 学习 React, completed: false }, { id: 2, text: 学习 useReducer, completed: false } ] }); const [inputText, setInputText] useState(); const addTodo () { if (inputText.trim()) { dispatch({ type: ACTIONS.ADD_TODO, payload: { id: Date.now(), text: inputText, completed: false } }); setInputText(); } }; return ( div div input value{inputText} onChange{(e) setInputText(e.target.value)} onKeyPress{(e) e.key Enter addTodo()} / button onClick{addTodo}添加/button /div ul {state.todos.map(todo ( li key{todo.id} input typecheckbox checked{todo.completed} onChange{() dispatch({ type: ACTIONS.TOGGLE_TODO, payload: todo.id })} / span style{{ textDecoration: todo.completed ? line-through : none }} {todo.text} /span button onClick{() dispatch({ type: ACTIONS.DELETE_TODO, payload: todo.id })} 删除 /button /li ))} /ul button onClick{() dispatch({ type: ACTIONS.CLEAR_COMPLETED })} 清除已完成 /button /div ); }三、惰性初始化3.1 基础用法// 初始化函数 function init(initialCount) { // 可以从 localStorage 读取 const saved localStorage.getItem(count); return { count: saved ? parseInt(saved) : initialCount }; } function reducer(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(reducer, 0, init); // 保存到 localStorage useEffect(() { localStorage.setItem(count, state.count); }, [state.count]); return ( div p计数: {state.count}/p button onClick{() dispatch({ type: increment })}/button button onClick{() dispatch({ type: decrement })}-/button /div ); }3.2 复杂初始化// 从多个来源初始化状态 function initState(initialState) { const savedState localStorage.getItem(app-state); if (savedState) { return JSON.parse(savedState); } // 从 URL 参数读取 const params new URLSearchParams(window.location.search); const userId params.get(userId); return { ...initialState, userId: userId || null }; } function AppReducer(state, action) { // ... } function App() { const [state, dispatch] useReducer( AppReducer, { user: null, theme: light, todos: [] }, initState ); }四、复杂状态管理4.1 嵌套状态const initialState { user: { profile: { name: , email: , avatar: null }, settings: { theme: light, notifications: true, language: zh-CN } }, ui: { sidebarOpen: true, modalOpen: false, loading: false } }; function appReducer(state, action) { switch (action.type) { case UPDATE_PROFILE: return { ...state, user: { ...state.user, profile: { ...state.user.profile, ...action.payload } } }; case UPDATE_SETTINGS: return { ...state, user: { ...state.user, settings: { ...state.user.settings, ...action.payload } } }; case TOGGLE_SIDEBAR: return { ...state, ui: { ...state.ui, sidebarOpen: !state.ui.sidebarOpen } }; default: return state; } }4.2 数组操作function todoReducer(state, action) { switch (action.type) { // 添加 case ADD: return [...state, action.payload]; // 删除 case REMOVE: return state.filter(item item.id ! action.payload); // 更新 case UPDATE: return state.map(item item.id action.payload.id ? { ...item, ...action.payload.data } : item ); // 批量更新 case UPDATE_MANY: return state.map(item { const update action.payload.find(u u.id item.id); return update ? { ...item, ...update.data } : item; }); // 排序 case SORT: return [...state].sort((a, b) { if (action.payload asc) return a.id - b.id; return b.id - a.id; }); // 过滤 case FILTER: return state.filter(item item.completed action.payload); default: return state; } }4.3 异步操作const initialState { data: null, loading: false, error: null }; function dataReducer(state, action) { switch (action.type) { case FETCH_START: return { ...state, loading: true, error: null }; case FETCH_SUCCESS: return { ...state, loading: false, data: action.payload }; case FETCH_ERROR: return { ...state, loading: false, error: action.payload }; default: return state; } } function DataFetcher({ url }) { const [state, dispatch] useReducer(dataReducer, initialState); useEffect(() { const fetchData async () { dispatch({ type: FETCH_START }); try { const response await fetch(url); const data await response.json(); dispatch({ type: FETCH_SUCCESS, payload: data }); } catch (error) { dispatch({ type: FETCH_ERROR, payload: error.message }); } }; fetchData(); }, [url]); if (state.loading) return div加载中.../div; if (state.error) return div错误: {state.error}/div; return pre{JSON.stringify(state.data, null, 2)}/pre; }五、useReducer vs useState5.1 何时使用 useReducer场景推荐原因简单状态布尔、数字、字符串useState代码更简洁多个相关状态useReducer逻辑集中管理复杂状态转换useReducerreducer 更清晰状态依赖之前的状态两者皆可useReducer 更安全深层嵌套状态useReducer更新逻辑更清晰5.2 对比示例// useState 版本 function FormWithState() { const [formData, setFormData] useState({ name: , email: , age: }); const updateField (field, value) { setFormData(prev ({ ...prev, [field]: value })); }; const reset () { setFormData({ name: , email: , age: }); }; // ... } // useReducer 版本 const ACTIONS { UPDATE_FIELD: UPDATE_FIELD, RESET: RESET }; function formReducer(state, action) { switch (action.type) { case ACTIONS.UPDATE_FIELD: return { ...state, [action.field]: action.value }; case ACTIONS.RESET: return { name: , email: , age: }; default: return state; } } function FormWithReducer() { const [formData, dispatch] useReducer(formReducer, { name: , email: , age: }); const updateField (field, value) { dispatch({ type: ACTIONS.UPDATE_FIELD, field, value }); }; const reset () { dispatch({ type: ACTIONS.RESET }); }; }六、性能优化6.1 稳定 dispatch// dispatch 函数是稳定的不会在重新渲染时改变 function Component() { const [state, dispatch] useReducer(reducer, initialState); // ✅ dispatch 可以安全地作为依赖 useEffect(() { dispatch({ type: INIT }); }, [dispatch]); // 不会导致无限循环 }6.2 拆分 Reducer// 将大的 reducer 拆分成小的 const userReducer (state, action) { switch (action.type) { case SET_USER: return { ...state, user: action.payload }; default: return state; } }; const uiReducer (state, action) { switch (action.type) { case TOGGLE_SIDEBAR: return { ...state, sidebarOpen: !state.sidebarOpen }; default: return state; } }; // 组合 reducer function rootReducer(state, action) { return { user: userReducer(state.user, action), ui: uiReducer(state.ui, action) }; } const initialState { user: { user: null }, ui: { sidebarOpen: true } };七、常见陷阱7.1 直接修改状态// ❌ 错误直接修改 function badReducer(state, action) { switch (action.type) { case ADD: state.todos.push(action.payload); // 直接修改 return state; } } // ✅ 正确返回新对象 function goodReducer(state, action) { switch (action.type) { case ADD: return { ...state, todos: [...state.todos, action.payload] }; } }7.2 忘记处理默认情况// ❌ 错误忘记返回 state function reducer(state, action) { switch (action.type) { case INCREMENT: return { count: state.count 1 }; // 缺少 default会返回 undefined } } // ✅ 正确始终返回 state function reducer(state, action) { switch (action.type) { case INCREMENT: return { count: state.count 1 }; default: return state; // 重要 } }八、练习题基础题实现一个计数器支持 1、-1、重置、步长设置实现一个购物车支持添加、删除、修改数量进阶题实现一个表单状态管理支持字段验证实现一个撤销/重做功能参考答案// 购物车 Reducer const cartReducer (state, action) { switch (action.type) { case ADD_ITEM: { const existing state.items.find(item item.id action.payload.id); if (existing) { return { ...state, items: state.items.map(item item.id action.payload.id ? { ...item, quantity: item.quantity 1 } : item ) }; } return { ...state, items: [...state.items, { ...action.payload, quantity: 1 }] }; } case REMOVE_ITEM: return { ...state, items: state.items.filter(item item.id ! action.payload) }; case UPDATE_QUANTITY: return { ...state, items: state.items.map(item item.id action.payload.id ? { ...item, quantity: Math.max(0, action.payload.quantity) } : item ).filter(item item.quantity 0) }; case CLEAR_CART: return { ...state, items: [] }; default: return state; } };九、小结要点说明Reducer纯函数根据 action 返回新状态Action包含 type 和 payload 的对象Dispatch触发状态更新的函数惰性初始化用于复杂初始状态计算核心要点useReducer 适用于复杂状态逻辑Reducer 必须是纯函数始终返回新对象不要直接修改可以配合 Context 实现全局状态管理