大家好,我是前端西瓜哥。今天來聊聊 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);
這種模式的特定是 同步執行。
分為兩種情況讨論:
- React 的流程中的 setState,我們。比如生命周期函數、React 的事件響應函數;
- 遊離在 React 控制之外的 setState。比如定時器的觸發、DOM 原生事件;
如果在 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
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 的更新是異步的。
我是前端西瓜哥,歡迎關注我,學習更多前端知識。