前言
对于有react使用经验的都知道,react的setState是异步执行的,就是react对state的改变是批量进行更新的,不是同步的,这样在一起的程度上也可以提高性能,把多次render合并成一次render。
不是所有的setState都是异步
上面所讲到setstate是异步的,其实是针对react中调用的方法而言,例如react的生命周期函数中,给元素绑定的事件中,例如:
[javascript] view plain copy
class App extends React.Component {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
this.state = {
val: 0,
};
}
handleClick() {
this.setState({val: 1});
}
render() {
return (
<p>
<span>{this.state.val}</span>
<button onClick={this.handleClick}>change val</button>
</p>
)
}
}
当我点击按钮的时候调用this.setState({val: 1});,React就会将this.state.val更新成1,并且自动帮我们更新UI。
如果点击按钮的时候我们连续调用setState会怎么样?React是连续更新两次,还是只更新一次呢?为了更好的观察出React的更新机制,我们将点击按钮的逻辑换成下面的代码
[javascript] view plain copy
this.setState({val: 1});
console.log(this.state.val);
this.setState({val: 2});
console.log(this.state.val);
打开控制台,点击按钮你会发现打印了0 0,同时页面数据也更新成了2。所以我们就得出结论:React的更新并不是同步的,而是批量更新的。
我们别急着下结论,我们知道应用程序状态的改变主要是下面三种情况引起的:
Events - 如点击按钮
Timers - 如setTimeout
XHR - 从服务器获取数据
我们才测试了事件这一种情景,我们试着看看其余两种情景下state的变化,将点击按钮的逻辑换成如下代码
[javascript] view plain copy
setTimeout(() => {
this.setState({val: 1});
console.log(this.state.val);
this.setState({val: 2});
console.log(this.state.val);
});
打开控制台,点击按钮你会发现打印了1 2,相信这个时候很多人就懵了,为啥和第一种情况的输出不一致,不是说好的批量更新的么,怎么变成连续更新了。
我们再试试第三种情景XHR,将点击按钮的逻辑换成下面的代码
[javascript] view plain copy
fetch('/')
.then(() => {
this.setState({val: 1});
console.log(this.state.val);
this.setState({val: 2});
console.log(this.state.val);
});
打开控制台,点击按钮你会发现打印的还是1 2,这究竟是什么情况?如果仔细观察的话,你会发现上面的输出符合一个规律:在React调用的方法中连续setState走的是批量更新,此外走的是连续更新。
为了验证这个的猜想,我们试着在React的生命周期方法中连续调用setState
[javascript] view plain copy
componentDidMount() {
this.setState({val: 1});
console.log(this.state.val);
this.setState({val: 2});
console.log(this.state.val);
}
打开控制台你会发现打印了0 0 ,更加验证了我们的猜想,因为生命周期方法也是React调用的。到此我们可以得出这样一个结论:
在React调用的方法中连续setState走的是批量更新,此外走的是连续更新
说到这里,有些人可能会有这样一个疑惑
[javascript] view plain copy
handleClick() {
setTimeout(() => {
this.setState({val: 1});
console.log(this.state.val);
this.setState({val: 2});
console.log(this.state.val);
});
}
setTimeout也是在handleClick当中调用的,为啥不是批量更新呢?
setTimeout确实是在handleClick当中调用的,但是两个setState可不是在handleClick当中调用的,它们是在传递给setTimeout的参数——匿名函数中执行的,走的是事件轮询,不要弄混了。
综上,说setState是异步的需要加一个前提条件,在React调用的方法中执行,这时我们需要通过回调获取到最新的state
[javascript] view plain copy
this.setState({val: 1}, () => {
console.log(this.state.val);
});