React setState非同步更新问题的一丢丢见解
在React中,通过setState来实现状态的修改更新,当this.setState()方法被调用后,会rerender实现视图的更新。整个流程如下图所示:
需要注意的是,setState对state的更新并非同步实现的,所以有时在调用this.state后直接获取修改的state值会发现依旧是更新前的值。
分析
首先明确三点:
- 1.setState不会立刻改变React组件中state的值
- 2.setState通过触发一次组件的更新来引发重绘
- 3.多次setState函数调用产生的效果会合并
这是使用this.setstate需要get的三个基础点,一点一点进行分析
第一,为什么不会立刻改变?
首先明确,在React中,如果是由React引发的事件处理(比如通过onClick引发的合成事件处理)和组件生命周期函数内(比如componentDidMount),调用this.setState不会同步更新this.state,除此之外的setState调用会同步执行this.state。
所谓“除此之外”,指的是绕过React通过addEventListener直接添加的事件处理函数,还有通过setTimeout/setInterval产生的异步调用。
为什么由React引发的就不会同步?
在React的setState函数实现中,会根据一个变量isBatchingUpdates
判断是直接更新this.state还是放到队列中回头再说,而isBatchingUpdates
默认是false
,也就表示setState会同步更新this.state,但是,有一个函数batchedUpdates
,这个函数会把isBatchingUpdates
修改为true,而当React在调用事件处理函数和自身生命周期之前就会调用这个batchedUpdates
,也就是说,任何通过React引发的更新,都会触发这个batchedUpdates
函数,导致isBatchingUpdates
是true
,导致不会立即更新。而其他绕过了React的,则是可以立即更新的。
第二第三,为什么要合并触发?
性能更好啊,相当于节流,因为vdom算法和视图更新都是需要耗费性能的,所以应当尽量减少。
解决
那么如果我需要绕过这个batchedUpdates
,或者说我就是需要立即执行,那么应该怎么办?
三点:
- 1.使用回调函数
- 2.使用setTimeout
- 3.和渲染无关的状态尽量不要放在 state 中来管理
1、回调
setState 方法接收一个 function 作为回调函数。这个回掉函数会在 setState 完成以后直接调用,这样就可以获取最新的 state 。对于之前的例子,就可以这样:
this.setState({
selection: value
}, this.fireOnSelect)
2、定时
定时器为什么可避免这个问题,上面已经说过了
3、和渲染无关的状态尽量不要放在 state 中来管理
通常 state 中只来管理和渲染有关的状态 ,从而保证 setState 改变的状态都是和渲染有关的状态。这样子就可以避免不必要的重复渲染。其他和渲染无关的状态,可以直接以属性的形式保存在组件中,在需要的时候调用和改变,不会造成渲染。