在React开发中,组件通信一直是核心话题。常见的通信方式有:
- 父子组件:通过props传递数据 👨👦
- 子父组件:通过回调函数通信 🔄
- 兄弟组件:通过父组件中转 📦
- 跨层级组件:使用Context或Redux等全局方案 🌐
但当应用复杂度上升时,单纯使用useContext
管理状态就像试图用筷子喝汤------力不从心!这时候,useReducer
就该登场了。它像一个 "状态管理专家",能把复杂的状态逻辑集中起来,用 "规则"(reducer 函数)管理状态变化,让代码从 "混乱的自由生长" 变成 "有序的制度管理"。
一、先吐槽下 useState 的 "小尴尬" 🤔
useState
是 React 中最基础的状态管理 Hook,简单场景下很好用,比如管理一个计数器:
jsx
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p> count: {count} </p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
但当状态变复杂(比如多个相关状态、修改逻辑繁琐),useState
就有点力不从心了:
- 状态修改逻辑分散 :比如一个表单有
name
、age
、email
,修改每个字段的逻辑都要写一个setXXX
,代码显得乱; - 状态依赖复杂 :如果修改
A
状态需要依赖B
和C
状态,setA
里的逻辑会越来越长,可读性差; - 调试困难 :想知道状态是怎么从
1
变成5
的?得逐个找setCount
的调用处,像在找 "谁动了我的奶酪"。
二、useReducer 登场:给状态管理 "定规矩" 🛠️
useReducer
是 React 提供的另一个状态管理 Hook,它的核心思想是:把状态修改的逻辑集中到一个 "reducer 函数" 里,通过 " action 命令" 来触发修改。就像公司里的 "流程化管理"------ 想做什么事(修改状态),得先填个单子(action),交给专门的部门(reducer)处理,避免混乱。
(1)useReducer 三要素:状态、 reducer、 dispatch
用 useReducer
管理状态,需要三个核心部分:
- 初始状态(initialState) :状态的初始值,比如
{ count: 0 }
; - reducer 函数:一个 "纯函数",负责根据 "命令"(action)计算新状态;
- dispatch 方法:用于发送 "命令"(action),触发 reducer 计算新状态。
直接上代码感受下(还是计数器,但用 useReducer
实现):
jsx
// 1. 初始状态:定义状态的初始值
const initialState = { count: 0 };
// 2. reducer 函数:集中处理状态修改逻辑(纯函数!)
// 接收两个参数:当前状态(state)、命令(action)
const reducer = (state, action) => {
// action 是一个对象,必须有 type 字段(表示命令类型)
switch (action.type) {
case 'increment': // 命令:加 1
return { ...state, count: state.count + 1 };
case 'decrement': // 命令:减 1
return { ...state, count: state.count - 1 };
case 'addNum': // 命令:加指定数值(带参数)
return { ...state, count: state.count + action.payload };
default: // 默认返回原状态(防止传错命令)
return state;
}
};
// 3. 使用 useReducer
function Counter() {
// 解构出:当前状态(state)、发送命令的方法(dispatch)
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p> count: {state.count} </p>
{/* 发送命令:调用 dispatch,传入 action 对象 */}
<button onClick={() => dispatch({ type: 'increment' })}>+1</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-1</button>
<button onClick={() => dispatch({ type: 'addNum', payload: 5 })}>+5</button>
</div>
);
}


(2)核心逻辑:action 是 "命令", reducer 是 "执行者" 📦
- action :一个普通对象,必须有
type
字段(字符串,表示命令类型,比如'increment'
),还可以带payload
字段(传递额外参数,比如+5
里的5
); - reducer :接收
state
(当前状态)和action
(命令),返回新的状态 (注意:不能直接修改原 state,要返回一个新对象,比如{ ...state, count: ... }
); - dispatch :调用
dispatch(action)
时,React 会自动调用 reducer,传入当前 state 和 action,计算出新 state 后,重新渲染组件。
(3)为什么说 reducer 必须是 "纯函数"? 📜
reducer
函数有个严格要求:必须是纯函数。 比如下面这个 reducer 就是 "不纯的",会导致状态混乱:
jsx
// 错误示例:直接修改原 state(不纯!)
const badReducer = (state, action) => {
if (action.type === 'increment') {
state.count += 1; // 直接改原 state,React 可能检测不到状态变化
return state;
}
return state;
};
纯函数的好处是:状态变化可预测、可追溯,调试时只要看 action
就能知道 "谁做了什么操作",像查日志一样清晰!
三、useReducer 对比 useState:什么时候该选它?
场景 | useState 适合 | useReducer 适合 |
---|---|---|
状态复杂度 | 简单状态(单个数字、字符串、布尔值) | 复杂状态(多个相关字段、嵌套对象) |
修改逻辑 | 简单(直接 count + 1 ) |
复杂(需要判断、依赖其他状态、多步骤) |
状态关联性 | 状态之间独立(如 name 和 age 无关) |
状态之间有关联(如 total = price * count ) |
调试需求 | 低 | 高(需要追踪状态变化原因) |
四、总结:useReducer 是 "规矩",也是 "自由" 🎉
useReducer
看似多了很多 "规矩"(action、reducer、纯函数),但这些规矩反而带来了 "自由"------ 当项目变大、多人协作时,统一的状态管理流程能减少混乱,让代码更易维护。
最后记住:
useReducer
的核心是 "集中管理状态修改逻辑",通过 action 驱动变化;- reducer 必须是纯函数,保证状态变化可预测;
- 简单状态用
useState
足够,复杂状态优先考虑useReducer
; - 配合
useContext
能轻松实现全局状态共享。
下次写组件时,如果发现 setXXX
越来越多,不妨试试 useReducer
------ 给状态管理 "定个规矩",你会发现代码突然变清爽了! 😎