You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中createRoot与ReactDOM.render的setState同步异步差异原因

React中createRoot与ReactDOM.render的setState同步/异步行为差异解析

核心原因是React 18引入createRoot时,彻底重构了状态更新的批处理策略,和旧版ReactDOM.render的规则完全不同。

先看ReactDOM.render(React 17及旧模式)的输出逻辑:

你的代码里,componentDidMount属于React的生命周期回调,处于React的同步更新上下文:

  • 前两次setState会被React合并批处理,不会立即更新state,所以连续两次console.log(this.state.count)输出的都是初始值0。
  • 批处理执行完成后,state会被更新为1(两次setState都是基于初始0累加,合并后只生效一次+1)。
  • 而setTimeout属于浏览器宏任务,脱离了React的更新上下文,这里的setState会同步执行更新:
    • 第一次setState把1变成2,立即更新state,所以console.log输出2;
    • 第二次setState把2变成3,同样立即更新,console.log输出3。
      最终输出就是0,0,2,3。

再看createRoot(React 18新模式)的输出逻辑:

React 18默认开启了自动批处理(Automatic Batching),不管代码是在生命周期、合成事件,还是setTimeout、Promise这类异步回调里,所有setState都会被自动批处理:

  • 前两次componentDidMount里的setState依然被批处理,console.log输出初始值0,批处理后state更新为1。
  • 关键差异在setTimeout里的两次setState:它们也被React自动批处理了,不会立即更新state。所以两次setState都是基于当前的1来计算,但在批处理执行前,state不会变化,两次console.log输出的都是1。等批处理完成后,state才会被更新为2,但这时候console.log已经执行完了。
    最终输出就是0,0,1,1。

额外补充:

如果在React 18里想取消某段代码的批处理,可以用ReactDOM.flushSync()包裹setState,这样就能回到旧模式的同步更新行为。

内容的提问来源于stack exchange,提问作者ChenLee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 09:55:12