本篇文章只是为了回答徒弟@河北小女孩 的一个问题,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 了,所以会直接更新。所以就是同步执行。
记住了么@河北小女孩 ?
文章评论