React18中handleClick与handleClick2渲染次数差异原因咨询
React18中handleClick与handleClick2的渲染差异解析
代码示例
useEffect(() => { console.log("render"); }); const handleClick = () => { setC1((c) => c + 1); Promise.resolve().then(() => { setC1((c) => c + 1); }); }; const handleClick2 = () => { Promise.resolve().then(() => { setC1((c) => c + 1); }); setC1((c) => c + 1); };
问题
在React18版本中,为何点击handleClick方法会触发两次渲染,而点击handleClick2方法仅触发一次渲染?原本预期两种方法的执行结果一致,二者存在差异的原因是什么?
核心原因:微任务队列顺序与React批处理机制的交互
React18的自动批处理会将同一上下文内的多个状态更新合并为一次渲染,但这一机制的效果会受浏览器事件循环中微任务的执行顺序影响:
1. handleClick的执行流程
- 同步调用
setC1((c) => c + 1):React将该更新加入待处理队列,并调度一个微任务(记为微任务A)来处理更新。 - 调用
Promise.resolve().then(...):将回调函数加入微任务队列(记为微任务B),此时微任务队列的顺序是微任务A在前,微任务B在后。 - 点击事件的宏任务执行完毕后,浏览器按顺序执行微任务:
- 先执行
微任务A:React处理队列中的第一个更新,触发第一次渲染。 - 再执行
微任务B:调用第二个setC1,此时没有活跃的批处理上下文,React直接处理该更新,触发第二次渲染。
- 先执行
2. handleClick2的执行流程
- 调用
Promise.resolve().then(...):将回调函数加入微任务队列(记为微任务B)。 - 同步调用
setC1((c) => c + 1):React将该更新加入待处理队列,并调度一个微任务(记为微任务A),此时微任务队列的顺序是微任务B在前,微任务A在后。 - 点击事件的宏任务执行完毕后,浏览器按顺序执行微任务:
- 先执行
微任务B:调用第二个setC1,该更新被加入同一个待处理队列。 - 再执行
微任务A:React处理队列中的两个更新(由于是函数式更新,会合并计算为c + 2),仅触发一次渲染。
- 先执行
关键总结
两种方法的差异本质是微任务的注册顺序不同,导致后续的状态更新是否能被纳入同一个批处理队列:
handleClick中,同步更新的调度微任务先进入队列,导致两次更新被分别处理,触发两次渲染。handleClick2中,Promise回调的微任务先进入队列,使得两次更新被纳入同一个批处理队列,最终合并为一次渲染。
内容的提问来源于stack exchange,提问作者1 2
相关产品推荐
相关产品推荐

