今天咱们来聊聊React中的状态管理,这就像管理一家公司——小公司靠老板吼(useState),大公司靠制度管(useReducer)!先看看组件间通信的几种方式:
🧩 组件通信的"职场关系学"
- 父子组件:像老板给员工派任务,通过props下发指令
<Child task={missionImpossible} />
2. 子父组件:员工提交报告,通过自定义事件回调
const Child = ({ onSubmit }) => (
<button onClick={() => submit('KPI达标!')}>汇报</button>
)
3. 兄弟组件:需要经过"老父亲"中转,像两个熊孩子传纸条被班主任截获
<Parent>
<BrotherA onMessage={setMsg} />
<BrotherB msg={msg} />
</Parent>
4. 跨层级通信:当公司规模扩大,需要建立集团制度
* 普通方案:`useContext` + `createContext` → 部门墙太厚,文件传递效率低
* 终极方案:`useReducer` → 建立全集团标准化流程
🧐 为什么需要useReducer?
想象你管理一家跨国企业(大型应用),如果每个部门(组件)都:
// 混乱的状态管理现场
<LoginContext.Provider>
<ThemeContext.Provider>
<TodosContext.Provider>
<Layout>...</Layout>
</TodosContext.Provider>
</ThemeContext.Provider>
</LoginContext.Provider>
这种"俄罗斯套娃式"管理会导致:
- 🤯 嵌套地狱(Prop Drilling)
- 🔄 状态更新难以追踪
- 🎲 数据流像没舵的船——随时翻
这样虽然可以实现我们的跨层级数据通信,但是代码的可读性实在太差,在大型项目中,我们需要的数据太多,这种方式不可取
🦸 useReducer登场:公司的标准化制度
它由三部分组成:
- initialState - 公司章程
- reducer - 法务部(纯函数)
- dispatch - 内部办公系统
// 1. 定义公司章程(初始状态)
const initialState = { count: 0 }
// 2. 建立法务部(reducer纯函数)
const reducer = (state, action) => {
switch (action.type) {
case '涨工资':
return { count: state.count + 1 }
case '扣绩效':
return { count: state.count - 1 }
default:
return state
}
}
// 3. 启用办公系统
const [state, dispatch] = useReducer(reducer, initialState)
// 财务部发起流程(组件内触发)
<button onClick={() => dispatch({ type: '涨工资' })}>
申请加薪
</button>
⚖️ 为什么reducer必须是"纯函数"?
纯函数 相同地的输入,一定会有相同的输出,没有副作用,不操作外部变量、不发送请求、不改DOM,管理数据状态 纯函数去管理, 全局状态更正确
看个反面教材——不守规矩的会计:
let total = 0 // 公司小金库
function addToTotal(a) {
total += a // 偷偷改外部变量(查账会出问题!)
return total
}
而优秀的法务部(纯函数):
// 相同输入永远得到相同输出
function add(a, b) {
return a + b // 光明正大算账
}
纯函数的三大纪律:
- 不操作外部变量(不碰小金库)
- 不发送请求(不私自联系客户)
- 不改DOM(不擅自装修办公室)
🚀 useReducer实战:双计数器管理系统
假设公司有两个部门需要独立核算:
// 初始化集团财务
const initialState = {
count1: 0, // 研发部资金池
count2: 10 // 市场部资金池
}
// 集团审计制度(reducer)
function reducer(state, action) {
switch (action.type) {
case '研发部加薪':
return { ...state, count1: state.count1 + 1 }
case '市场部加薪':
return { ...state, count2: state.count2 + 1 }
case '全员调薪':
return {
count1: state.count1=action.payload,
count2: state.conut2=action.payload
}
// ...其他财务流程
}
}
function CFO() { // 财务总监组件
const [state, dispatch] = useReducer(reducer, initialState)
const [input, setInput] = useState(0)
return (
<>
<div>
<h3>研发部资金: {state.count1}</h3>
<button onClick={() => dispatch({ type: '研发部加薪' })}>
申请研发部调薪
</button>
</div>
<div>
<h3>市场部资金: {state.count2}</h3>
<button onClick={() => dispatch({ type: '市场部加薪' })}>
申请市场部调薪
</button>
</div>
<input
value={count}
onChange={e => setConut(e.target.value)}
placeholder="输入全员薪资标准"
/>
<button onClick={() => dispatch({
type: '全员调薪',
payload: parseInt(count)
})}>
执行全员调薪
</button>
</>
)
}
🔍 执行流程解析(以调薪为例):
-
财务输入调薪数额:
setCount(10000) -
点击按钮发起申请:
dispatch({ type: '全员调薪', payload: 10000 }) -
法务部(reducer)处理申请:
case '全员调薪': return { count1: action.payload, // 研发部调薪 count2: action.payload // 市场部调薪 } -
生成新状态触发重新渲染
-
两个部门的资金显示同时更新
🆚 useState vs useReducer 抉择指南
| 场景 | useState(小卖部) | useReducer(沃尔玛) |
|---|---|---|
| 状态类型 | 独立简单值 | 关联复杂对象 |
| 更新逻辑 | 直接set | 通过action派发 |
| 数据流追踪 | 较困难 | 清晰可追踪 |
| 跨组件更新 | 需配合context | 天然适合 |
| 可维护性 | 小型项目友好 | 中大型项目必备 |
💡 最佳实践心得
-
像立法一样设计action:
// 反例 - 含糊不清 dispatch({ type: '改数字', value: 10 }) // 正例 - 语义明确 dispatch({ type: '设置研发预算', payload: 1000000 }) -
永远返回新对象:
// 错误!直接修改原状态(相当于做假账) state.count = 100 return state // 正确!返回全新状态对象 return { ...state, count: 100 }
count: 100 是你修改的具体某个状态会覆盖...state中原始的
-
拆分大reducer:
// 当制度太复杂时(超过200行) const rootReducer = combineReducers({ finance: financeReducer, // 财务制度 hr: hrReducer // 人事制度 })
🌟 总结
用useReducer管理状态就像建立现代企业制度:
- dispatch 是OA办公系统(流程可控)
- reducer 是法务部(确保合规)
- action 是标准化表单(减少沟通成本)
下次当你的组件树开始变成"俄罗斯套娃",当useState的回调函数蔓延成"面条代码",请记得召唤useReducer这位管理大师!毕竟——好的程序员不写代码,他们设计制度。🫡
今日金句:用useState是手工作坊,用useReducer是工业化生产,而用Redux...那是建立跨国集团!