setState可以说是React中使用频率最高的一个函数了,我们都知道,React是通过管理状态来实现对组件的管理的,当this.setState()被调用的时候,React会重新调用render方法来重新渲染UI
但实际使用的时候,我们会发现,有时候我们setState之后,并没有立刻生效,例如我们看一下以下的示例代码
class Test extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
componentDidMount() {
this.setState({count: this.state.count + 1});
console.log(this.state.count); // 输出0
this.setState({count: this.state.count + 1});
console.log(this.state.count); // 输出0
setTimeout(() => {
this.setState({count: this.state.count + 1});
console.log(this.state.count); // 输出2
this.setState({count: this.state.count + 1});
console.log(this.state.count); // 输出3
}, 0);
}
render() {
return <div> test </div>;
}
}
开发过程中我们会发现,在componentDidMount方法中,我们调用setState之后state的值并没有立即改变,但如果我们在setTimeOut里面调用,我们却能立刻就能获得更新,原因就在于react中的使用了基于事务(传送门,关于事务原理的解析)的异步更新机制,但对于这个异步的理解,又跟ajax的异步有所不同,因为毕竟react是一个js框架,所有的操作都是单线程的,所有的操作,都得按顺序来,那么它具体是怎么实现的呢?
我们都知道,对于dom的操作对性能的损耗是非常严重的,所以react为了提高整体的渲染性能,会将一次渲染周期中的state进行合并,在这个渲染周期中你对所有setState的所有调用都会被合并起来之后,再一次性的渲染,这样可以避免频繁的调用setState导致频繁的操作dom,提高渲染性能。具体的实现方面,可以简单的理解为react中存在一个状态变量isBatchingUpdates,当处于渲染周期开始时,这个变量会被设置成true,渲染周期结束时,会被设置成false,react会根据这个状态变量,当出在渲染周期中时,仅仅只是将当前的改变缓存起来,等到渲染周期结束时,再一次性的全部render,,具体的流程可以参照下面的流程图
现在,我们回到最开始的问题,为什么一开始在componentDidMount中直接执行setState会无法立刻得到更新呢,原因就在于,我们在componentDidMount中其实处于首次渲染的事务当中,这次事务的渲染尚未完成,而首次渲染的时候会将isBatchingUpdates设置为true,这是我们在componentDidMount中调用setState,react会发现当前事务尚未完成,只会直接将修改后的state放入到dirtyComponents中,等待最终渲染周期完成时,将所有的state进行合并,一次性render。而当我们放在setTimeOut里面的时候,setTimeOut会将操作放到执行队列的最后方,也就是说会等待渲染周期结束之后再进行setState,这个时候状态变量已经被重置回来了,所以此时我们的每一次setState都会立刻生效
那么,问题来了,当我们在渲染周期中执行了setState之后,我们要如何获取到最新的state的值呢,setTimeOut是一个方案,但是不太优雅,有没有其他方法呢,我们注意到,setState提供了一个回调函数,我们只需要在回调里面获取更新后的state即可,像这样
componentDidMount() {
this.setState({count: this.state.count + 1},()=>{
console.log(this.state.count);//该是啥就是是啥
}));
}
————————————————
原文链接:https://blog.csdn.net/handsomexiaominge/article/details/86348235