useReducer 是 React 中用于管理复杂状态逻辑的 Hook,尤其适合处理具有多个子值的状态对象、存在复杂状态转换逻辑或多个操作影响同一状态的场景。它的工作方式类似 Redux 的 reducer 模式,通过 action 驱动状态更新,让状态变化逻辑更可预测、可维护。

核心概念

  • state:当前的状态值
  • action:一个描述“要做什么”的普通对象(通常包含 type 字段表示操作类型,以及其他必要数据)
  • reducer:一个纯函数,接收当前 stateaction,返回新的 state(state, action) => newState
  • dispatch:用于发送 action 的函数(调用 dispatch(action) 触发 reducer 执行)

基础用法

1. 定义 reducer 函数

reducer 是纯函数,根据 action.type 决定如何更新状态,不能直接修改原状态,必须返回新状态。

// 定义 reducer:根据 action 处理状态
function todoReducer(state, action) {switch (action.type) {case 'ADD_TODO':// 返回新数组(不修改原数组)return [...state,{ id: Date.now(), text: action.text, done: false }];case 'TOGGLE_TODO':// 映射新数组,只修改目标项return state.map(todo =>todo.id === action.id ? { ...todo, done: !todo.done } : todo);case 'DELETE_TODO':// 过滤掉要删除的项return state.filter(todo => todo.id !== action.id);default:// 未知 action 时返回原状态return state;}
}
2. 在组件中使用 useReducer
import { useReducer } from 'react';function TodoApp() {// 初始化状态(空数组),获取 [状态, dispatch函数]const [todos, dispatch] = useReducer(todoReducer, []);const [inputText, setInputText] = useState('');// 处理添加任务const handleAdd = () => {if (!inputText.trim()) return;// 发送 ADD_TODO 动作,携带必要数据dispatch({ type: 'ADD_TODO', text: inputText });setInputText(''); // 清空输入框};return (<div><inputvalue={inputText}onChange={(e) => setInputText(e.target.value)}placeholder="输入任务..."/><button onClick={handleAdd}>添加</button><ul>{todos.map(todo => (<likey={todo.id}style={{ textDecoration: todo.done ? 'line-through' : 'none' }}onClick={() => dispatch({ type: 'TOGGLE_TODO', id: todo.id })}>{todo.text}<button onClick={() => dispatch({ type: 'DELETE_TODO', id: todo.id })}>删除</button></li>))}</ul></div>);
}

复杂场景优势

当状态逻辑复杂时(例如多字段联动、条件判断多、状态依赖前一个状态),useReduceruseState 更清晰:

示例:购物车状态管理
// 初始状态
const initialState = {items: [], // 商品列表total: 0,  // 总价count: 0   // 商品总数
};// 复杂状态逻辑的 reducer
function cartReducer(state, action) {switch (action.type) {case 'ADD_ITEM': {const existingItem = state.items.find(item => item.id === action.item.id);let newItems;if (existingItem) {// 商品已存在,更新数量newItems = state.items.map(item =>item.id === action.item.id ? { ...item, quantity: item.quantity + 1 } : item);} else {// 新商品,添加到列表newItems = [...state.items, { ...action.item, quantity: 1 }];}// 计算新的总数和总价const count = newItems.reduce((sum, item) => sum + item.quantity, 0);const total = newItems.reduce((sum, item) => sum + (item.price * item.quantity), 0);return { ...state, items: newItems, count, total };}case 'REMOVE_ITEM': {// 实现删除商品逻辑...}default:return state;}
}// 组件中使用
function ShoppingCart() {const [cart, dispatch] = useReducer(cartReducer, initialState);const addToCart = (product) => {dispatch({ type: 'ADD_ITEM', item: product });};// ...渲染购物车
}

useState 的对比

场景

推荐使用

原因

简单状态(单值)

useState

语法更简洁,无需定义 reducer

复杂状态(多字段/联动)

useReducer

状态逻辑集中在 reducer 中,便于调试和复用,避免多个 setState 嵌套

需要预测状态变化

useReducer

action 可追踪,方便回溯状态变更历史

注意事项

  1. reducer 必须是纯函数:不能修改原 state、不能执行副作用(如请求、定时器),仅根据输入计算输出。
  2. 状态更新是批量的:多次 dispatch 会合并处理,类似 setState
  3. 初始化状态:可直接传初始值,或通过函数返回(适合复杂初始化逻辑):
// 函数式初始化(只执行一次)
const [state, dispatch] = useReducer(reducer, null, () => {return { count: localStorage.getItem('count') || 0 };
});

通过 useReducer,可以将复杂状态逻辑从组件中抽离,让组件更专注于 UI 渲染,同时使状态变化更可预测、易于测试。