连续调用三次 setCount,为什么有时只加一?
先理解原理和示例,再练习完整口述与追问。
回答要点
同一处理函数里的三次 setCount(count + 1) 使用相同快照,重复请求同一个结果;三次 setCount(c => c + 1) 则按更新队列逐次计算。批处理范围与 React 版本和 root 模式有关。
原理拆解
当本次 count 为 0,三次 setCount(count+1) 都计算成“设为 1”,更新队列中重复替换为同一个值,不是依次把闭包里的 count 递增。函数式 updater 则接收队列中前一步计算后的状态,React 依次执行,因而可以累加。updater 必须纯净,不能在里面发请求或写日志作为唯一业务副作用。
示例与推演
// 同一事件中,当前 count 为 0
setCount(count + 1)
setCount(count + 1)
setCount(count + 1) // 结果为 1
// 若从 0 开始改用下列三次:
setCount(n => n + 1)
setCount(n => n + 1)
setCount(n => n + 1) // 结果为 3
这两组应分别分析,不能把六句连续执行还期待得到 3;混合替换值和 updater 要逐项推导队列。
易错点
不同用户点击不是同一轮无条件合并。开发模式可能额外调用 updater 检查纯度,但不会要求你人为扣掉一次更新。
参考回答可直接练习口述
同一个事件处理器中 count 不变,连续三次 setCount(count + 1) 实际是重复放入同一个替换结果,因此常见结果只加一。三次 setCount(c => c + 1) 会按队列把每次计算结果交给下一个 updater,所以加三。React18 使用 createRoot 后自动批处理范围扩大,旧 root 的行为不能直接照搬。
面试官可能继续问追问与完整解答展开收起
追问
替换值和 updater 混用时结果如何?为什么不要在 updater 里做副作用?React 官方更新队列
追问回答
队列中替换值会设置当前待处理结果,后续 updater 再基于该结果计算;例如从 0 开始先 setCount(5),再加一,最后替换成 10,结果是 10。updater 必须纯净,因为它可能在开发检查或重试渲染时被再次执行。