本篇文章只是为了回答徒弟@河北小女孩 的一个问题,setState 是同步的还是异步的?
翻阅了一下react-dom的源码外加上自己的经验,总结出以下两点:
- 同步代码下异步执行
- 异步代码下同步执行
同步代码下异步执行
在非事件回调和setTimeout下,比如react的生命周期中,setState 的代码是异步执行的。
constructor() {
super();
this.state = {
val: 0,
};
}
componentDidMount() {
console.log(this.state.val, '1'); // 0
this.setState({
val: this.state.val + 1,
});
this.setState({
val: this.state.val + 1,
});
console.log(this.state.val, '2'); // 0
console.log(this.state.val, '3'); // 0
}
componentDidUpdate() {
console.log('did');
console.log(this.state.val);
}
原因是:react源码中有一个 isBatchingUpdates: false,在每个事务开始前会被置为 true,事务结束后的close会被再置回false;
isBatchingUpdates 为 true,所以并不会直接执行更新 state,而是加入了 dirtyComponents,,等待后续执行,所以此时会被异步挂起。所以打印时获取的都是更新前的状态 0。
还有一个重点,多个异步的setState 会被合并执行,所以componentDidUpdate 只会被执行一次,且是 1。

异步代码同步执行
如果你的setState是被setTimeout包裹的,或者是事件函数中的回调fn,那setState就会被同步的执行。且不会被合并执行,会导致组件被渲染多次。
componentDidMount() {
setTimeout(() => {
console.log(this.state.val, '1');
this.setState({
val: this.state.val + 1,
});
this.setState({
val: this.state.val + 1,
});
console.log(this.state.val, '2');
console.log(this.state.val, '3');
}, 0);
}
componentDidUpdate() {
console.log('did');
console.log(this.state.val);
}
所以执行结果就是
0 "1" did 1 did 2 2 "did" 2 "did"
setState 同步执行,且每次都会重新render组件。
源码解释 因为 回调函数异步执行 已经是在事务close之后了,这个时候 isBatchingUpdates 已经是false 了,所以会直接更新。所以就是同步执行。
记住了么@河北小女孩 ?
文章评论