前言
对于有react使用经验的都知道,react的setState是异步执行的,就是react对state的改变是批量进行更新的,不是同步的,这样在一起的程度上也可以提高性能,把多次render合并成一次render。
不是所有的setState都是异步
上面所讲到setstate是异步的,其实是针对react中调用的方法而言,例如react的生命周期函数中,给元素绑定的事件中,例如:
class App extends React.Component { constructor() { super(); this.handleClick = this.handleClick.bind(this); this.state = { val: 0, }; } handleClick() { this.setState({val: 1}); } render() { return ( <div> <span>{this.state.val}</span> <button onClick={this.handleClick}>change val</button> </div> ) } }
当我点击按钮的时候调用this.setState({val: 1});
,React就会将this.state.val
更新成1
,并且自动帮我们更新UI。
如果点击按钮的时候我们连续调用setState
会怎么样?React是连续更新两次,还是只更新一次呢?为了更好的观察出React的更新机制,我们将点击按钮的逻辑换成下面的代码
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的变化,将点击按钮的逻辑换成如下代码
setTimeout(() => { this.setState({val: 1}); console.log(this.state.val); this.setState({val: 2}); console.log(this.state.val); });打开控制台,点击按钮你会发现打印了
1 2
,相信这个时候很多人就懵了,为啥和第一种情况的输出不一致,不是说好的批量更新的么,怎么变成连续更新了。
我们再试试第三种情景XHR,将点击按钮的逻辑换成下面的代码
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
componentDidMount() { this.setState({val: 1}); console.log(this.state.val); this.setState({val: 2}); console.log(this.state.val); }
打开控制台你会发现打印了0 0
,更加验证了我们的猜想,因为生命周期方法也是React调用的。到此我们可以得出这样一个结论:
在React调用的方法中连续setState走的是批量更新,此外走的是连续更新
说到这里,有些人可能会有这样一个疑惑
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
this.setState({val: 1}, () => { console.log(this.state.val); });
参考:https://segmentfault.com/a/1190000006986141