React的setState

前言

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

猜你喜欢

转载自blog.csdn.net/chiuwingyan/article/details/80220157