0%

已完成 0/29 关

教学模式

你可以随时调整。

此设置调整教师的教学方式,无需重新生成课程。

第 24 关 含代码 含表格 约 10 分钟

迁移状态逻辑至 Reducer:dispatch 与 useReducer 重构

迁移状态逻辑至 Reducer:dispatch 与 useReducer 重构

随着你的组件功能越来越丰富,组件里的状态更新逻辑也会变得越来越庞大繁琐。例如,在一个任务管理应用中,你可能需要处理“添加任务”、“编辑任务文本”、“勾选已完成”、“删除任务”四五种操作。

如果把这四五种操作的数组过滤、展开复制全部塞在不同的事件处理函数中,组件的代码会变得异常臃肿,而且很难一眼看清这个组件到底支持哪些业务行为。

为了解决这个问题,React 提供了 Reducer 机制:把所有的状态计算逻辑,从组件函数中全部抽离出来,集中存放在一个干净纯粹的单一函数中。


1. 迁移到 Reducer 的标准三步法

第一步:将“设置状态”改为“分发 Action”

以往我们是直接命令计算机怎么改数据:

function handleDeleteTask(taskId) {
  setTasks(tasks.filter(t => t.id !== taskId));
}

使用 Reducer 时,你不再告诉 React“怎么更新数组”,而是告诉 React“用户刚刚做了什么操作”。这种描述用户行为的对象,被称为 Action。 通过调用 dispatch(分发) 函数来派发这个 Action:

function handleDeleteTask(taskId) {
  dispatch({
    type: 'deleted', // 动作类型(通常用过去分词或动词)
    id: taskId       // 附带的操作数据
  });
}

第二步:编写一个纯函数 Reducer(Reducer Function)

Reducer 是一个普通的 JavaScript 函数,它接收两个参数:当前的状态(state) 以及 刚刚分发的动作(action),并计算出下一个全新的状态并返回:

function tasksReducer(tasks, action) {
  switch (action.type) {
    case 'added': {
      return [
        ...tasks,
        {
          id: action.id,
          text: action.text,
          done: false
        }
      ];
    }
    case 'changed': {
      return tasks.map(t => {
        if (t.id === action.task.id) {
          return action.task;
        } else {
          return t;
        }
      });
    }
    case 'deleted': {
      return tasks.filter(t => t.id !== action.id);
    }
    default: {
      throw Error('未知的 action 类型: ' + action.type);
    }
  }
}

Reducer 铁律:Reducer 函数必须是绝对纯净的纯函数! 它不能包含异步请求、不能包含定时器、不能直接变异原状态。它只负责根据当前状态和动作,计算并返回新状态。


第三步:在组件中使用 useReducer Hook

从 React 中引入 useReducer:

import { useReducer } from 'react';

把原本的 useState 替换为 useReducer:

// 之前:
// const [tasks, setTasks] = useState(initialTasks);

// 现在:传入你的 reducer 函数以及初始状态
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);

现在,你的组件内部代码变得极其清爽:所有的按钮点击,都只需要一行短短的 dispatch({ type: '...' })!所有复杂的数组变更逻辑全都被移到了组件外部的 tasksReducer 中,你甚至可以把这个 reducer 提取到单独的测试文件中进行纯单元测试!


2. useState vs useReducer:如何选择?

权衡维度useStateuseReducer
代码体积初始代码很少,适合几个简单独立的值需要提前写 reducer 函数与 action,初始模版稍多
可读性状态逻辑简单时极具可读性当状态变更逻辑复杂且分支众多时,可读性极佳
调试体验散落的 setter 较难统一步骤排查可以在 reducer 里打印日志,每一步 action 清晰可循
可测试性逻辑与组件紧密耦合,较难单独测试Reducer 是纯函数,可以独立导入进行单元测试

经验法则:如果你发现一个组件里经常有几个状态需要同时更新,或者单个状态的修改方式超过了 3 种,果断重构为 useReducer!


总结

  • Reducer 将组件内部杂乱的分散状态更新逻辑,统一收拢为单一纯函数;
  • 不再就地直接修改状态,而是分发表达意图的 action 对象(dispatch({ type: '...' }));
  • Reducer 函数接收 (state, action) 并返回计算出的新 state;
  • useReducer 让大型组件的数据流向变得无比清晰规整。
下一站:使用 Context 深层传递参数:告别 Prop 逐级透传 →
© 2026 React 官方教程