最近看了程墨的几篇文章,对setState这个API有了更加深入的认识,下面总结一下。
先回顾一下React组件的生命周期。
一、组件的生命周期
组件生命周期在挂载期首先会调用componentWillMount(),然后会接着触发render()渲染到页面,最后调用componentDidMount(),在卸载前会调用componentWillUnmont(),然后卸载组件。
在组件更期时(此处指以props改变为例),首先会从父组件传来props,触发钩子函数(hook)componentWillReceiveProps()(若是state改变不会触发),然后触发shouldComponentUpdate(),如果shouldComponentUpdate()函数返回false,这时候更新过程就被中断了,render函数也不会被调用了,这时候React不会放弃掉对this.state的更新的,所以虽然不调用render,依然会更新this.state。若返回的是true,就会紧接着触发componentWillUpdate(),接着触发render(),更新组件的渲染,最后触发触发componentDidUpdate(),组件卸载和上述流程一样。
二、setState()介绍
引用网上的说法:
React抽象来说,就是一个公式
[图片上传失败...(image-cfb7ee-1531880117865)]
setState是React用来更新state的一个API,用得越多,发现setState()有很多让人入坑的地方:
- 使用setState()一般情况下(后面会介绍特殊情况)不会立即更新state的值;
- setState通过引发一次组件的更新过程来引发重新绘制;
- 多次setState函数调用产生的效果会合并。
对于第一点,引用网上的例子:
function incrementMultiple() {
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
}
上面的函数运行时,似乎对state.count的值加了3次,但实际上只加了一次,原因正是setState不会立即改变state的值所以后面两次的this.state其实还是最初的state。
第二点需要提到上面的组件更新期的声明周期,setState调用引起的React的更新生命周期函数4个函数:
- shouldComponentUpdate()
- componentWillUpdate()
- render()
- componentDidUpdate()
当shouldComponentUpdate函数被调用的时候,this.state没有得到更新。
当componentWillUpdate函数被调用的时候,this.state依然没有得到更新。
直到render函数被调用的时候,this.state才得到更新。
(或者,当shouldComponentUpdate函数返回false,这时候更新过程就被中断了,render函数也不会被调用了,这时候React不会放弃掉对this.state的更新的,所以虽然不调用render,依然会更新this.state。)
所以setState一般不会立即更新state的值,知道render()函数触发。
对于第三点可以简单理解为:
连续调用了两次this.setState,但是只会引发一次更新生命周期,不是两次,因为React会将多个this.setState产生的修改放在一个队列里,缓一缓,攒在一起,觉得差不多了再引发一次更新过程。这样做的好处是,不用每次setState就去触发一次render(),这样太消耗性能。
另外setState()可以接受一个函数作为参数,也就是说可以在里面参入回调函数:
function increment(state, props) {
return {count: state.count + 1};
}
function incrementMultiple() {
this.setState(increment);
this.setState(increment);
this.setState(increment);
}
值得注意的是:同样是把状态中的count加1,但是函数increment的状态的来源不是this.state,而是输入参数state,所以加1是累加上去的。
值得一提的是,在increment函数被调用时,this.state并没有被改变,依然,要等到render函数被重新执行时(或者shouldComponentUpdate函数返回false之后)才被改变。这也就是说如果, this.setState(increment)中插入 this.setState({count: this.state.count + 1})会让前面的努力白费。如下:
function incrementMultiple() {
this.setState(increment);
this.setState(increment);
this.setState({count: this.state.count + 1});
this.setState(increment);
}
在几个函数式setState调用中插入一个传统式setState调用(嗯,我们姑且这么称呼以前的setState使用方式),最后得到的结果是让this.state.count增加了2,而不是增加4,所以不宜混用两种发法。
网友评论