React state:把计数器真正看懂 · 第 2 / 3 节

第 02 节:连续更新为什么会挤在同一个旧值上

本节目标:

  • 解释为什么 setCount(count + 1) 连写两次可能只加 1 。
  • 使用函数式更新让每次更新基于上一版 state。
  • 修掉一个“目标加 2,结果只加 1”的按钮。

先修:上一节的 state 与 setter 分工 | 上一节 << 01 | 下一节 03 >>

两次调用不等于读到两个新值

React 会等事件处理函数里的代码跑完,再处理 state 更新队列1。如果你在同一次点击里连写两次 setCount(count + 1),这两个表达式读到的可能都是同一个旧 count

这里的关键不是“React 慢”,而是“你读到的是本次渲染里的值”。如果两次都从旧值 0 算 0 + 1,最后就不是 2 。

讲解

React 文档给了一个专门的写法:把函数传给 setter,例如 setNumber(n => n + 1)。这个函数会在处理队列时拿到上一版 state,再算下一版1

可以把 c => c + 1 读成“别用我现在闭包里看到的旧 count,等 React 处理队列时,把上一版 state 交给我”。这就是函数式更新。

跟我做一遍

坏掉的版本:

如果点击前 count 是 0,两行都读到 0,两行都请求下一次变成 1 。修法是:

同一次点击里的行这行读到的 count这行请求下一次显示
第 1 行 setCount(count + 1)01
第 2 行 setCount(count + 1)01

这张表先把坏现象钉住:两行不是接力从 0 到 1 再到 2,而是都拿同一个旧值算。下面才进入修法。

第一条 updater 从 0 算到 1,第二条 updater 从上一条结果 1 算到 2 。这里的“上一条结果”来自更新队列,不是来自你手动改了 count 变量。

换你补全

把下面两行补成函数式更新:

参考答案:

留空点不在变量名,而在“把一个 updater 函数交给 setter”这个机制。

小结 + 通向下一节

连续 state 更新最稳的写法是 updater 函数,因为它让每一步基于上一版 state。下一节把这个计数器放进 Next.js:按钮能动还需要一个 Client Component 边界。

Footnotes

  1. React Learn: Queueing a Series of State Updates — https://react.dev/learn/queueing-a-series-of-state-updates 2

Exercises

Exercise 1 · Level 1

写一段解释,说明为什么 setCount(count + 1) 连写两次容易只加 1 。

Done criteria · checked locally
Exercise 2 · Level 2

把“加 3”按钮写成函数式更新版本。

Done criteria · checked locally
Extensions of this lesson