大家好,我是前端西瓜哥。今天来聊聊 React 的 setState 是同步还是异步的。
其实 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);
这种模式的特定是 同步执行。
分为两种情况讨论:
如果在 React 流程中,setState 是批量延后执行的。
例子:
componentDidMount() {
console.log("setState 前:", this.state.count);
this.setState({
count: this.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({
count: this.state.count + 1
});
console.log("setState 后:", this.state.count);
});
}
输出结果为:
setState 前:0
setState 后:1
这里用 setTimeout 是脱离 React 流程的,此时 setState 会做同步更新,立即更新状态。
如果你希望在 React 流程外也做批量更新,可以用 React.unstable_batchedUpdates
进行包裹,效果类似在 React 流程中,会延迟同步执行。
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 的更新是异步的。
我是前端西瓜哥,欢迎关注我,学习更多前端知识。