React useReducer 复杂状态

10 阅读2分钟

React useReducer 复杂状态

前置:React Context 跨层传值
目标:会写 reducer(state, action);理解何时从 useState 升级。

引言

一个开关、一个输入框 → useState 最爽。
状态字段变多、更新规则互相牵扯时,一堆 setX / setY 容易乱。

useReducer:把「怎么从旧状态算出新状态」收进纯函数 reducer(state, action);组件里只 dispatch({ type: '...' })。

本篇 demo:计数器 + 可调步长 + 重置,四种 action。

动手

完整代码:react2/12-usereducer/demo

cd react2/12-usereducer/demo
npm install && npm run dev

改步长再点 +step / -step;点「重置」回到 count=0, step=1。


1. 定义 state / action / reducer

type State = { count: number; step: number }
type Action =
  | { type: 'inc' }
  | { type: 'dec' }
  | { type: 'setStep'; step: number }
  | { type: 'reset' }

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'inc':
      return { ...state, count: state.count + state.step }
    case 'dec':
      return { ...state, count: state.count - state.step }
    case 'setStep':
      return { ...state, step: action.step }
    case 'reset':
      return { count: 0, step: 1 }
    default:
      return state
  }
}

约定:

  • 纯函数:同样输入 → 同样输出;不写副作用
  • 不可变更新:返回新对象,别直接改 state.count = ...
  • action.type 描述「发生了什么」,不是「把 count 设成几」

2. 组件里 dispatch

const [state, dispatch] = useReducer(reducer, { count: 0, step: 1 })

<button onClick={() => dispatch({ type: 'inc' })}>+step</button>
<button onClick={() => dispatch({ type: 'dec' })}>-step</button>
<button onClick={() => dispatch({ type: 'reset' })}>重置</button>
<input
  type="number"
  value={state.step}
  onChange={(e) =>
    dispatch({ type: 'setStep', step: Number(e.target.value) || 1 })
  }
/>

UI 只发意图;怎么改状态全在 reducer。


3. 何时从 useState 升级

继续 useState考虑 useReducer
单个布尔 / 字符串下一状态严重依赖上一状态
字段少、更新简单同一状态有多种「动作」
想把更新逻辑抽到组件外测

useReducer + Context 常一起做中等复杂度的客户端状态;更大更碎的全局状态看 Zustand(15 篇)。


4. 常见坑

做法问题
reducer 里 fetch / 改 DOM副作用放错地方;副作用进 event / effect
直接 mutate stateReact 可能检测不到更新
action 设计成「setCount(5)」一堆丢失「意图」;能工作但难推理
简单开关也上 reducer过度;useState 更短

demo 在练什么

  1. 手写 State / Action / reducer
  2. 用 dispatch 驱动多动作更新
  3. 体会「步长影响加减」这种跨字段规则放在 reducer 里更清晰

自己改一改:加 type: 'double';或把初始 state 换成惰性初始化 useReducer(reducer, undefined, init)。


下一篇

React 嵌套路由与布局