您现在的位置:首页 >> 前端 >> 内容

React的setState异步执行讲解

时间:2018/5/9 16:28:41 点击:

  核心提示:前言对于有react使用经验的都知道,react的setState是异步执行的,就是react对state的改变是批量进行更新的,不是同步的,这样在一起的程度上也可以提高性能,把多次render合并成...

前言

对于有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);  

});  

Tags:RE EA AC CT 
作者:网络 来源:chiuwingya