ReactJS stale closure问题:快速点击时setTimeout调用hooks仅触发2次重渲染
问题核心原因
1. 过时闭包捕获了旧状态
你快速多次点击按钮时,所有触发的onClosure函数都来自val = 0的初始渲染上下文,每个onClosure中注册的setTimeout回调捕获的val永远是初始值0,不会随后续状态更新变化。
2. 所有状态更新请求值相同
1000ms后所有定时器回调依次执行时:
- 每个回调里的
console.log语句打印的都是捕获到的旧值0,所以你点击多少次就会输出多少组before = 0、inside = 0的日志,对应你观测到的「setTimeout内部语句均被正常执行」的现象。 - 每个回调执行
setVal(val + 1)等价于setVal(0 + 1),也就是所有的状态更新请求都是把val设置为1。
3. React状态更新的跳过逻辑
- 页面加载时已经完成一次初始渲染,打印
outside = 0。 - 第一个定时器回调执行时,React检测到
val从0变为1,触发一次重渲染,打印outside = 1。 - 后续所有定时器回调执行
setVal(1)时,当前的val已经是1,状态没有发生变更,React会直接跳过重渲染,不会产生新的outside日志。 - 算上初始渲染,你总共只会观测到2次渲染日志,也就是你说的「仅触发2次重渲染」的现象。
修复方案
如果要实现点击N次最终val变为N的效果,把状态更新改为函数式写法即可,函数式更新可以拿到最新的状态值,不受过时闭包影响:
setVal(prev => prev + 1)
内容的提问来源于stack exchange,提问作者GyuMin Han
相关产品推荐
相关产品推荐

