React学习之useReducer

44 阅读2分钟

useReducer 是 React 提供的一个内置 Hook,用于在函数组件中管理复杂的状态逻辑。它类似于 Redux 的简化版,适用于状态更新逻辑较复杂、多个子值依赖彼此、或需要可预测状态变化的场景。

一、基本使用方法

1. 语法

const [state, dispatch] = useReducer(reducer, initialState, init);
  • reducer:一个纯函数,接收当前 state 和 action,返回新的 state。
  • initialState:初始状态。
  • init(可选) :用于惰性初始化的函数,initialState = init(initialArg)

2. 示例

"use client"import React, { useReducer } from 'react';
​
​
type actionType = {
  type: string;
  value?: any;
}
​
const initialState = {
  count: 0,
  name: 'Reducer Example',
};
const reducer = (state: any, action: actionType) => {
  switch (action.type) {
    case 'count':
      return { ...state, count: action.value };
    case 'name':
      return { ...state, name: action.value };
    default:
      return state;
  }
};
​
const ReducerPage = () => {
​
  const [state, dispatch] = useReducer(reducer, initialState);
​
  return <div>
    <h1>{state.name}</h1>
    <p>Count: {state.count}</p>
    <button onClick={() => dispatch({ type: 'count', value: state.count + 1 })}>Increment Count</button>
    <button
      onClick={() => dispatch({
        type: 'name',
        value: `Updated Reducer Example: ${new Date().toLocaleTimeString()}`
      })}
    >
      Change Name
    </button>
  </div>;
};
​
export default ReducerPage;

二、使用场景

1. 状态逻辑复杂

useState 难以维护(例如状态包含多个子属性、更新逻辑相互依赖)时,useReducer 更清晰。

2. 多个组件共享状态更新逻辑

配合 useContext 可实现“类 Redux” 的全局状态管理,避免 props drilling。

3. 需要可预测、可测试的状态转换

reducer 是纯函数,便于单元测试;所有状态变更通过 dispatch(action) 触发,便于追踪。

4. 表单状态管理

如多步骤表单、动态字段增删等,用 useReducer 可集中处理字段变更、验证、重置等逻辑。

三、注意事项

1. reducer 必须是纯函数

  • 不能有副作用(如 API 调用、直接修改 state)。
  • 相同输入必须返回相同输出。

2. 不要直接修改 state

始终返回新对象,而不是修改原 state:

// ❌ 错误
state.count++;
return state;
​
// ✅ 正确
return { ...state, count: state.count + 1 };

3. 性能优化

  • 如果 reducer 计算开销大,可考虑 useMemo 缓存中间结果(但 reducer 本身通常很快)。
  • dispatch 函数在组件 re-render 期间是稳定的(不会变),可安全用于依赖数组(如 useEffect)。

4. 与 useState 的选择

  • 简单状态 → useState
  • 复杂状态逻辑、多个相关状态、需要集中管理 → useReducer

5. 调试支持

可通过在 reducer 中加日志,或使用 React DevTools 查看 dispatch 的 action。

四、进阶:惰性初始化(Lazy Initialization)

如果初始状态计算开销大,可用第三个参数 init

const init = (initialCount) => ({ count: initialCount });
​
function reducer(state, action) { /* ... */ }
​
function Counter({ initialCount }) {
  const [state, dispatch] = useReducer(reducer, initialCount, init);
}

这样 init 只在首次渲染时执行一次。

总结

特性useStateuseReducer
适用场景简单独立状态复杂、关联状态
状态更新直接设值或函数通过 dispatch(action)
可读性简单场景更直观复杂逻辑更清晰
测试性较弱强(纯函数)

合理使用 useReducer 能让状态管理更健壮、可维护。