← 返回目录

第 5 卷

第 5 / 28 卷日期:

  1. 渲染器执行这些 DOM 操作指令,将更新应用到真实 DOM 中,完成页面的更新。
  2. 执行 setState 的回调函数:
  3. 真实 DOM 更新完成后,React 会调用 setState 的第二个参数(回调函数),在该回调函数中

可以获取到更新后的 this.state(因为此时状态和 DOM 都已完成更新)。

十、setState 调用是同步还是异步的?

setState 的调用「既不是绝对同步,也不是绝对异步」,而是「默认异步,特定场景下同步」,核心取决于调用 setState 时的「执行环境」,即是否处于 React 自身的批量更新上下文控制中。

1. 默认异步场景(大部分日常开发场景)

当 setState 调用处于 React 控制的执行环境中时,表现为异步更新,此时无法在 setState 调用后立即获取到新状态。

• 常见场景:

  • React 合成事件回调(如 onClick、 onChange、 onSubmit 等)。

• React 生命周期方法(如 componentDidMount、 componentDidUpdate、

shouldComponentUpdate 等,注意 componentWillMount 已废弃)。

  • React 钩子函数内部(如 useEffect、 useLayoutEffect 等)。

示例:

Code block

1class Example extends React.Component {
2state = { count: 0 };
3handleClick = () => {
4this.setState({ count: this.state.count + 1 });
5console.log(this.state.count);//输出0(异步更新,此时状态未更新)
6};
7render() {
8return <button onClick={this.handleClick}>点击</button>;
9}
10}

原因:React 在此类场景下会开启批量更新,将多个 setState 调用合并为一次更新,减少 DOM 操

调用后立即获取到新状态。

• 常见场景:

  • 原生 DOM 事件回调(如 addEventListener 绑定的事件)。
  • 定时器回调(如 setTimeout、 setInterval)。
  • Promise 回调(如 Promise.then、 async/await 后续逻辑)。
  • 手动调用 setState 时的同步代码块(如直接在控制台调用)。

示例:

Code block

1class Example extends React.Component {
2state = { count: 0 };
3componentDidMount() {
4//原生DOM事件
5document.getElementById('btn').addEventListener('click', () => {
6this.setState({ count: this.state.count + 1 });
7console.log(this.state.count);//输出1(同步更新,立即获取新状态)
8});
9// setTimeout回调
10setTimeout(() => {
11this.setState({ count: this.state.count + 1 });
12console.log(this.state.count);//输出2(同步更新)
13}, 0);
14}
15render() {
16return <button id="btn">点击</button>;
17}
18}

原因:此类场景下,代码执行脱离了 React 的批量更新上下文,React 无法将更新加入批量队列,因此会立即触发状态更新流程, setState 调用后状态和 DOM 会同步更新。

3. React18 中的变化:自动批处理(Automatic Batching)

React18 之前,只有 React 控制的场景(合成事件、生命周期)支持批量更新;React18 后通过「自动批处理」特性,将批量更新的范围扩展到了原生事件、setTimeout、Promise 等所有场景,即大部

分原本同步的场景现在也支持异步批量更新,进一步优化性能。

2

3class Example extends React.Component {
4state = { count: 0 };
5handleClick = () => {
6ReactDOM.flushSync(() => {
7this.setState({ count: this.state.count + 1 });
8});
9console.log(this.state.count);//输出1(强制同步更新)
10};
11render() {
12return <button onClick={this.handleClick}>点击</button>;
13}
14}

总结

setState 的同步/异步取决于执行环境:

  • React 控制的环境(合成事件、生命周期):默认异步,批量更新。

• 非 React 控制的环境(原生事件、定时器、Promise):React18 前同步,React18 后默认异步

(自动批处理),可通过 flushSync 强制同步。

• 无论同步还是异步,都不建议依赖 setState 后的 this.state 做后续逻辑,如需使用更新

后的状态,优先使用 setState 的回调函数(第二个参数)或 componentDidUpdate 生命周期方法。

十一、React 中 setState 的批量更新过程具体实现

setState 的批量更新是 React 优化性能的核心机制之一,其核心思想是「延迟执行状态更新,合并多个更新请求,最终一次性触发渲染」,具体实现依赖于 React 内部的「批量更新上下文」和「更新队

列」管理,不同 React 版本(尤其是 React18)的实现范围有差异,具体过程如下:

1. 核心前提:批量更新上下文标识

React 内部通过一个全局标识(如 isBatchingUpdates)来标记当前是否处于「批量更新上下文」中。该标识由 React 控制,在特定场景下自动切换状态:

• 进入批量更新上下文时(如合成事件回调、生命周期方法执行前),将 isBatchingUpdates

设为 true。

• 退出批量更新上下文时(如合成事件回调、生命周期方法执行完成后),将

全站分卷索引 · 28 卷
  1. 1第 1 卷
  2. 2第 2 卷
  3. 3第 3 卷
  4. 4第 4 卷
  5. 5第 5 卷
  6. 6第 6 卷
  7. 7第 7 卷
  8. 8第 8 卷
  9. 9第 9 卷
  10. 10第 10 卷
  11. 11第 11 卷
  12. 12第 12 卷
  13. 13第 13 卷
  14. 14第 14 卷
  15. 15第 15 卷
  16. 16第 16 卷
  17. 17第 17 卷
  18. 18第 18 卷
  19. 19第 19 卷
  20. 20第 20 卷
  21. 21第 21 卷
  22. 22第 22 卷
  23. 23第 23 卷
  24. 24第 24 卷
  25. 25第 25 卷
  26. 26第 26 卷
  27. 27第 27 卷
  28. 28第 28 卷