这是一道经典 react 面试题,之前还有点疑惑,为了探究真理,特来加以总结。
答案:react 的 setState 既可能是同步的,也可能是异步的。 准确地说,在 React 内部机制能检测到的地方, setState 就是异步的;在 React 检测不到的地方,例如 setInterval,setTimeout 里,setState 就是同步更新的。
在介绍这个问题之前,我们先来看一下一个例子:
state = {
number:1
};
componentDidMount(){
this.setState({number:3})
console.log(this.state.number) //1
}
看完这个例子,也许很多小伙伴会下意识的以为 setState 是一个异步方法,但是其实 setState 并没有异步的说法,之所以会有一种异步方法的表现形式,归根结底还是因为 react 框架本身的性能机制所导致的。因为每次调用 setState 都会触发更新,异步操作是为了提高性能,将多个状态合并一起更新,减少 re-render 调用。
试想一下如果在组件中有以下这样一段代码执行:
for (let i = 0; i < 100; i++) {
this.setState({ num: this.state.num + 1 });
}
如果 setState 是一个同步执行的机制,那么这个组件会被重新渲染 100 次,这对性能是一个相当大的消耗。
显然,React 也是想到了这个问题,因此对 setState 做了一些特殊的优化:
❝ React会将多个setState的调用合并为一个来执行,也就是说,当执行setState的时候,state中的数据并不会马上更新
❞
这也很好的印证了刚才提到的那个例子。
但是往往在实际的开发工作中,我们可能需要同步的获取到更新之后的数据,那么怎么获取呢?下面介绍几种常用的方法:
回调函数
setState 提供了一个回调函数供开发者使用,在回调函数中,我们可以实时的获取到更新之后的数据。还是以刚才的例子做示范:
state = {
number:1
};
componentDidMount(){
this.setState({number:3},()=>{
console.log(this.state.number)//3
})
}
可以看见此时控制台打印的数据是最新的数据。这也完美的印证了我们的猜想是正确的。
总结:
❝ setState本身并不是异步,只是因为react的性能优化机制体现为异步。在react的生命周期函数或者作用域下为异步,在原生的环境下为同步。
❞