Part one - setState点燃引擎
这是一个React组件实现组件可交互所需的流程,render()输出虚拟DOM,虚拟DOM转为DOM,再在DOM上注册事件,事件触发setState()修改数据,在每次调用setState方法时,React会自动执行render方法来更新虚拟DOM,如果组件已经被渲染,那么还会更新到DOM中去。 这个过程,setState就像一个点燃引擎的打火石,发动了React核心的调度层,然后直至渲染层的改变。
Part two - setState是异步的
刚接触React的同学,对React的setState的使用偶尔会有一些偏颇,出现一些意料之外的情况。
比如:
onClickForReset=()=>{
this.setState({value: []});
// 此刻立马取this.state做一些同步操作
console.log(this.state.value);
}
或者是
increateCount(){
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
}
我们可以看一个现在的例子:
https://codesandbox.io/s/qqy9n5o2m9
setState比较熟练的同学可以跳过这一段代码,但是有些刚学会使用React的同学经常会犯这个错误,一开始我只能粗暴地说:
- setState是异步的,不会立即改变state的值。
- 多次setState调用生成的效果会合并。
- 第二个参数可以是一个回调函数。
- setState可以接受一个函数(例子改动)
后来我逐渐也在想下面这两个问题,现在这篇文章试图尽量弄清的两件事:
- 为什么要把setState设计成异步的,缘由是什么,解决了什么问题,有什么好处?
- 如何实现异步的setState,整体原理是怎样的,有没有什么特殊的骚操作?
我们可以自己也想一想,下面留给大家一片空白区。😝
<div style="background:#fff;width:100%;height:500px;margin-bottom:30px;"></div>
好,我们带着这两个问题和自己的猜想,试图一探究竟。
Part three - 为什么要异步
简单的来说:在批量多次的更新中,延缓到最后合并渲染是有好处的。
- 保证内部的一致性:首先,我想我们都同意推迟并批量处理重渲染是有益而且对性能优化很重要的,无论 setState() 是同步的还是异步的。那么就算让 state 同步更新,props 也不行,因为当父组件重渲染(re-render )了你才知道 props。
- 在批量多次的更新中,延缓到最后合并渲染是有好处的。这一点,和我们熟知的防抖动函数的出发点类似,我们普遍认为在许多情况下在同一时间段,频繁setState触发渲染,连续同步效率很低,对性能有极大损耗。
我们来看下setState引发组件的更新过程就知道了:
每一次setState如果都引发一次组件更新,走完一圈生命周期,实在是有点粗糙和浪费,生命周期函数为纯函数性能应当还能够接受,可是render函数内返回的虚拟DOM去做比较这个就比较费时间了。
直观的感受是,React将多个setState产生的修改放在一个队列里,缓一缓,攒在一起,等待时机,觉得差不多了再引发一次更新过程。这样,在每次更新过程中,会把积攒的setState结果合并,做一个merge的动作,节省render触发的频率。
这样,对于开发者而言,可以在同步代码中随意多行调用setState函数而不用担心重复setState重复render的问题。
然后,总是被大家误用不理解的也是这一点,所以后来,setState方法的第二个参数慢慢被进入大家的视野了,作为回调函数可以再次拿到新的this.state值。
再后来,一个setState函数的隐藏功能进入了大家的视野,那就是,setState可以接受一个函数作为参数。
更多:https://github.com/facebook/react/issues/11527#issuecomment-360199710
Part four - 怎么实现异步
在此之前,我们来看一个例子…可能会颠覆你对setState的认识
我们可以直接打开在线案例:https://codesandbox.io/s/vq1nqkvyw5
提问:实际运行结果是怎么样的?好吧现在下
Part five - 彩蛋:setState真的是异步吗?
我们上面一个例子告诉我们:
No! 可能是同步 超出React生命周期和React代理事件之外都是同步
预知后事如何,请看下半篇。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。