R23第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

副作用的适用场景

副作用的适用场景 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

写代码前先分类:这段逻辑是在响应用户动作,还是在做渲染计算,还是在跟外部系统同步?读完你应该能把放错位置的 Effect 挪回正确位置,并用渲染次数的变化证明挪对了。

问题场景

计数器满 3 的倍数时弹一个「连点 N 次!」的成就提示。第一版把它写成了 Effect:

const [count, setCount] = useState(0);
const [toast, setToast] = useState("");

useEffect(() => {           // 没有 deps:每次渲染之后都执行
  if (count > 0 && count % 3 === 0) {
    setToast(`连点 ${count} 次!`);
  }
});

能用,但每次点击都至少跑两轮:点击 → 渲染 → Effect → setToast → 再渲染。提示明明是对「用户点了按钮」的回应,却绕道渲染流程转了一圈。

核心解释

React 里只有三类位置,各有各的活:

渲染期计算:没有「发生」只有「算出」。状态、props 进来,UI 描述出去,必须纯。列表筛选、统计汇总(R17)属于这里。

事件处理:响应「用户做了什么」。点击、输入、提交都是明确的动作,处理逻辑放进对应的事件处理器——动作发生一次,逻辑跑一次,时机和次数都由用户控制。

Effect:同步「React 之外的世界」。和外部系统保持一致才轮到它:订阅消息源、同步 document.title、管理定时器。它跑在渲染之后,不是干业务逻辑的地方。

把成就提示挪回事件处理:

function handleClick() {
  const next = count + 1;
  setCount(next);
  if (next % 3 === 0) setToast(`连点 ${next} 次!`); // 响应用户动作,一次点击跑一次
}

收益马上可见:toast 的更新和 count 的更新合并在同一次渲染里,Effect 版多出来的那一整轮渲染消失了。更重要的是因果清楚——提示是点击造成的,而不是「渲染之后发现 count 是 3 的倍数」这个副产品。将来产品要加「提示只弹一次」,你改事件处理就够了;Effect 版还得防着各种路径绕进来。

判断口诀:这段逻辑如果是因为「用户做了某件事」才要跑,放事件处理;如果是因为「数据是那样」就该显示,放渲染期;只有「不管什么原因导致重新渲染,我都得让外部世界跟上来」,才放 Effect。

示例拆解

实验区并排两个计数器,各带渲染计数器:

  • 左边是 Effect 版:每次点击后 Effect 执行数 +1,弹提示那次更是连涨两次——因为它没有依赖数组,渲染几次跑几次,setToast 还额外触发一轮渲染;
  • 右边是事件版:没有 Effect,提示直接写在点击处理里,点击一次只渲染一次;
  • 两边的 toast 文案完全一样,差别全在渲染轮次和 Effect 执行计数上;
  • 验证方式:连点到 6,对比两侧「渲染次数」——右边始终等于点击数,左边明显更多。

动手任务

打开本课实验区,完成三个小改动:

  1. 给左边 Effect 版补上 [count] 依赖,观察 Effect 执行数的变化,再解释为什么仍然比事件版多渲染。
  2. 把 toast 增加自动消失:事件版在点击处理里用 setTimeout 关闭它(暂时不清理也没关系,R25 再收拾)。
  3. 说出实验里两个计数器各有一处「渲染期计算」(渲染次数的展示),确认它俩为什么不该用 Effect 实现。

常见错误

  • 用 Effect 监听状态变化去做「响应动作」的事:提示、跳转、发请求通知后端,这些因果源头是用户动作,放 Effect 会失去触发时机和次数的控制。
  • 用 Effect 链式同步多个 state:useEffect(() => setB(f(a)), [a]) 是渲染期计算的替身,直接渲染时算出 B 就行。
  • 把「每次渲染都要跑」当成需求:那通常说明你想做的是渲染期计算或外部同步,而不是无依赖的 Effect。
  • 在事件处理里干渲染期计算的活:把派生值在点击时 set 进 state,另一个入口改了数据就漏更新;先分类再落位。

检查与复盘

完成下方检查题确认理解。自查清单:

  • 能用「因果源头」判断逻辑归属:用户动作 → 事件,数据形状 → 渲染,外部世界 → Effect
  • 知道无依赖 Effect 每次渲染都跑,评估过自己的 Effect 是否配得上这个频率
  • 改完位置后会用渲染次数、日志这类可观察信号验证收益

参考资料

动手实验
动手实验
预览
点击「运行」在隔离沙箱中预览
检查题共 1 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1多选简单(多选,全部选对才算正确)

下列哪些情况不需要也不应该用 useEffect 处理?(多选)

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...