setState同步/异步详解
正确地使用 State
关于 setState()
你应该了解三件事:
1.不要直接修改 State
直接修改State
不会重新渲染组件:
// Wrong |
而是应该使用 setState()
:
// Correct |
构造函数是唯一可以给 this.state
赋值的地方。
setState函数定义在Component的原型对象上,
react -> ReactBaseClasses.js -> Component
2.setState异步更新
特殊情况下:setState是同步更新的
为什么setState要设计成异步更新?
- 首先,
setState设计成异步,可以显著提升性能
。- 如果每次调用setState都进行一次更新,那么意味着render函数会被频繁的调用,界面重新渲染,这样效率很低。
- 最好的办法是:获得多个更新,之后进行批量处理。
如果同步更新了State, 但是还没有执行render函数,那么state和props不能保持同步
;- 比如:父组件的state通过props传递给子组件,父组件改变State触发更新,但是此时传递给子组件的props还是上一次的值,这可能产生一些问题。
如何拿到最新的State状态?
有两种方式:
通过setState的第二个参数,传入一个回调函数callback,这个回调函数会在state状态更新后执行。
在componentDidUpdate生命周期函数中,获取最新的State的值。
源代码中是先执行生命周期函数,再执行回调的
有什么方法可以让setState同步更新?
有两种情况下,setState是同步更新的
- 将setState放入到异步方法中,如:setTimeout
setTimeout(() => { |
- 在原生的dom事件中,如点击事件。
所以setState的更新方式分为两种情况:
- 在组件生命周期或React合成事件中,setState是异步的。
- 在setTimeout或者原生dom事件中,setState是同步的。
源码
从上面的setState源码可以看到,setState方法其实调用的是this.updater.enqueueSetState
方法
这个updater是传递个每一个Class组件的参数。
它在源码中react-reconciler
这个包下面的ReactFiberClassComponent.old.js中

可以看到这个updater中有一个requestUpdateLane方法,这个方法,会根据当前的运行上下文,计算本次update的优先级。
之后requestUpdateLane函数会将优先级返回,赋值给lane
, 之后createUpdate函数会根据这个lane
创建一个update。
后面的 enqueueUpdate(fiber, update);
方法会将这个update挂载到对应的Fiber节点上
在将update挂载到对应的Febier节点后,就通过scheduleUpdateOnFiber调度本次更新。
参考:
- State & 生命周期 – React (docschina.org)
- React源码17.0.2