setState是同步和还是异步的

setState

setState立马执行之后,是无法直接获取到最新的state的,需要经过React对state的所有改变进行合并处理之后,才会去计算新的虚拟dom,再根据最新的虚拟dom去重新渲染真实dom。

1
2
3
4
5
6
7
8
9
10
11
12
13
14

this.setState({ count: this.state.count + 1 });
console.log("console: " + this.state.count); // 0
this.setState({ count: this.state.count + 1 }, () => {
console.log("console from callback: " + this.state.count); // 2
});
this.setState(prevState => {
console.log("console from func: " + prevState.count); // 1
return {
count: prevState.count + 1
};
}, ()=>{
console.log('last console: '+ this.state.count)
});

结果

1
2
3
4
console: 0 
console from func: 1
console from callback: 2
last console: 2

React 其实会维护着一个 state 的更新队列,每次调用 setState 都会先把当前修改的 state 推进这个队列,在最后,React 会对这个队列进行合并处理,然后去执行回调。根据最终的合并结果再去走下面的流程(更新虚拟dom,触发渲染)。

setState为什么要设计成“异步”的,因为setState()之后无法立马获取最新的 state,给人的感觉便是异步去设置状态。也确实是有异步的感觉。

合成事件

React为了解决跨平台,兼容性问题,自己封装了一套事件机制,代替了原生的事件,像在jsx中常见的onClick、onChange这些都是合成事件

原生事件

原生事件是指非React合成事件,原生自带的事件监听addEventListener,或者也可以用原生js,jquery直接document.querySelector().onclick 这种绑定事件的形式都属于原生事件。

点击执行,点一点理解就很清楚了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class App extends Component {
state = {
count: 0
};

componentDidMount() {
// 生命周期中调用
this.setState({ count: this.state.count + 1 });
console.log("lifecycle: " + this.state.count);
setTimeout(() => {
// setTimeout中调用
this.setState({ count: this.state.count + 1 });
console.log("setTimeout: " + this.state.count);
}, 0);
document.getElementById("div2").addEventListener("click", this.increment2);
}

increment = () => {
// 合成事件中调用
this.setState({ count: this.state.count + 1 });
console.log("react event: " + this.state.count);
};

increment2 = () => {
// 原生事件中调用
this.setState({ count: this.state.count + 1 });
console.log("dom event: " + this.state.count);
};

render() {
return (
<div className="App">
<h2>couont: {this.state.count}</h2>
<div id="div1" onClick={this.increment}>
click me and count+1
</div>
<div id="div2">click me and count+1</div>
</div>
);
}
}

image-20230506114929005

结论

下面的异步是带引号的

  1. setState 只在合成事件和钩子函数中的是异步的,在原生事件和setTimeout中都是同步的
  2. setState 的异步并不是说内部由异步代码实现,其实本身执行的过程中和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形成了所谓的异步,当然可以通过第二个参数setState(partialState, callback)中的callback拿到更新后的结果
  3. setState 的批量更新优化也是建立在异步(合成事件,钩子函数)只上的,在原生事件和setTimeout中不会批量更新,在异步中如果对同一个值进行多次setTimeout,setState的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同事setState对个不同的值,在更新时会对其进行合并批量更新

20210107补充

setState 异步的,性能优化过程中的batchedUpdates批处理,当触发事件,多个this.setState,只触发一次,多次合并为一次,提升react性能。

全局变量executionContext |= BatchedContext
setTimeOut, 全部的上下文不存在BathedContext,跳出,同步更新

以上是基于ReactDom.render(, rootElement) legacy模式

如果开启Concurrent模式则全是异步, ReactDom.unstable_createRoot(rootElement).render(App /)

image-20230506115008801
image-20230506115021870

参考

  1. https://juejin.im/post/5b45c57c51882519790c7441
感谢你的打赏哦!