React 的 setState 同步还是异步

83 阅读3分钟

文章首发于我的公众号:前端西瓜哥

大家好,我是前端西瓜哥。今天来聊聊 React 的 setState 是同步还是异步的。

Sync Mode

其实 React 官方叫 Legacy Mode(Legacy 表示过时的),但为了更好地表示这种模型的特性,我还是将它叫做 Sync(同步) Mode。

Sync Mode 是旧的同步不可中断的架构。使用 ReactDom.render 方法开启:

import ReactDOM from "react-dom";
import App from "./App";

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

这种模式的特定是 同步执行

分为两种情况讨论:

  1. React 的流程中的 setState,我们。比如生命周期函数、React 的事件响应函数;

  2. 游离在 React 控制之外的 setState。比如定时器的触发、DOM 原生事件;

如果在 React 流程中,setState 是批量延后执行的

例子:

componentDidMount() {
  console.log("setState 前:"this.state.count);

  this.setState({
    countthis.state.count + 1
  });

  console.log("setState 后:"this.state.count);
}

输出结果为:

setState 前:0
setState 后:0

可以看到 setState 并不是立即生效的,所以我们可以将其认为是异步的吗?然鹅并不是。

其实在这种情况下 React 是将 setState 要做的各种更新,先不立即更新,而是先保存起来,在声明周期函数的后期阶段才将这些更新的内容做一个合并,合并成一个对象,然后再去更新,是一种批量延后的行为。

它还是同步的,但是延后的同步。

如果在 React 流程外,setState 是立即同步更新

例子:

componentDidMount() {
  setTimeout(() => {
    console.log("setState 前:"this.state.count);

    this.setState({
      countthis.state.count + 1
    });

    console.log("setState 后:"this.state.count);
  });
}

输出结果为:

setState 前:0
setState 后:1

这里用 setTimeout 是脱离 React 流程的,此时 setState 会做同步更新,立即更新状态。

如果你希望在 React 流程外也做批量更新,可以用 React.unstable_batchedUpdates 进行包裹,效果类似在 React 流程中,会延迟同步执行。

Concurrent Mode

Concurrent Mode,并发模式。所谓并发,就是将 render 操作对应的大任务,拆分成一个个小任务,去异步执行,和其他任务表现为并发执行。

并发的意思,是在单线程的 JavaScript 中,将原本需要依次执行的多个任务,每个都拆分,每次只执行一小部分,看起来好像所有任务都在同时执行的感觉。

需要注意的是,并发并不是并行,并发只是因为速度很快,看起来像是同时进行而已。并行则是真正的有物理上的分身,真正的多个线程一起干活。

使用 createRoot 方式启用:

import ReactDOM from "react-dom";
import { createRoot } from "react-dom/client";

import App from "./App";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(<App />);

对于上面两种写法下,控制台输出都是:

setState 前:0
setState 后:0

在 React 流程中,setState 是并发的,即异步可中断

setState 后不会立即更新,不能立刻得到 state 的改变。

结尾

总结一下,同步模式(sync)下,React 流程中的 setState 更新操作是批量延迟同步的,流程外的 setState 是立即同步执行的。

使用并发模式(concurrent)下,使用了全新的 Fiber 架构,setState 的更新是异步的。

我是前端西瓜哥,欢迎关注我,学习更多前端知识。