关于React中的setState

在react中,setState是用以改变class组件状态的函数,它有两种用法:
一 传入一个updater函数,该函数有两个参数,一个是当前的state,还有一个是当前的props。该函数的返回值须要是一个更改的state值的对象,它将于state进行浅合并,其用法以下:html

this.setState((state, props) => {
        return { count: state.count + props.number };
    });

二 直接传入一个对象:react

this.setState({ count: this.state.count + this.props.number });

setState函数还能够接受第二个参数,该参数为一个函数,将在更改的State生效以后调用:git

console.log(this.state.count); // 1
    this.setState({ count: 0 }, () => {
        console.log(this.state.count); // 0
    });
    console.log(this.state.count); // ? 此处便可能是1,也多是0

从上面代码能够看到,最后一行输出的count是不固定的,这是为何呢?
由于在react中,class内的事件处理程序会默认进行批处理,即若是你在componentDidMount里面调用三次setState函数,那么它最终会在componentDidMount执行完毕后,将三个State的更改合并为一次调用。因此这时候setState就是异步的。
而在其余场景下,setState将会是同步的,例如setTimeout内, Promise的then里面。
一个简单的例子:github

class SetStateExample extends Component {
  constructor() {
    super();
    this.state = {
      count: 0
    };

    this.onClick = this.onClick.bind(this);
  }

  componentDidMount() {
    console.log('componentDidMount before', this.state.count);
    this.setState({ count: this.state.count + 1 });
    console.log('componentDidMount after', this.state.count);
  }

  onClick() {
    console.log('onClick before', this.state.count)
    this.setState({ count: this.state.count + 1 }, () => {
      console.log('setState callback', this.state.count);
    });
    console.log('onClick after', this.state.count);
    Promise.resolve().then(() => {
      console.log('promise.then before', this.state.count);
      this.setState({ count: this.state.count + 1 });
      console.log('promise.then after', this.state.count);
      this.onClassEvent();
    });
  }

  onClassEvent() {
    console.log('onClassEvent before', this.state.count);
    this.setState({ count: this.state.count + 1 });
    console.log('onClassEvent after', this.state.count);
  }

  render() {
    return <div className="test">
      <div>count: {this.state.count}</div>
      <button onClick={this.onClick}>点击改变count</button>
    </div>;
  }
}

让咱们运行结果:promise

clipboard.png

首先第一第二行输出是在componentDidMount里面,咱们在函数内调用了setState,并在先后分别输出了改变的值,结果代表,函数调用前与函数调用后该值并无当即改变,则代表在这里setState是一个异步调用。那么初步断定在生命周期函数内部,setState是异步的调用。异步

而后第三第四行输出是在onClick函数的回调里面,该函数定义在class中,经过用户点击触发。在setState调用先后咱们的输出结果是一致的,这也代表其是一个异步调用。而在setState第二个参数中咱们输出了改变后的count, 即第五行输出,代表咱们的更改生效了。函数

而后第六行之后的输出是咱们在onClick函数内调用了promise.resolve().then()输出的,它是一个异步调用,react是没法知道它何时执行,何时完成执行的,因此这时候react默认setState是同步的。从输出咱们能够看到每次更改以后,state的值都是当即变化生效的。性能

而在promise的回调内,咱们还调用了一个定义于class内的事件函数,可是该事件函数内的setState也是同步的形式。这说明了setState的同步或者异步与其定义位置并无直接的关系,而应该取决因而否由React直接进行调用,由于只有是React直接调用的状况下,它才知道该函数何时执行完毕,才能进行批处理的优化。不然则默认是同步的调用。(具体内部实现就不展开了,由于我也不是特别懂HHHH,反正意思就大概是这么个意思)优化

因此当在一些回调内部调用setState时应该注意将多个setState合并,由于它是同步的,屡次更新状态会很影响性能。
以及须要注意进行异步调用的时候,若是须要使用变化后的值,请确保在异步调用完成后,通常是在setState的回调内,或者在componentDidUpdate钩子内,可是请注意当心使用,由于很容易一不当心致使循环调用而崩溃。this

若是你想在本应同步调用的回调内,对setState进行异步调用,即让它进行批处理,React也提供了一个API:

promise.then(() => {
  // Forces batching
  ReactDOM.unstable_batchedUpdates(() => {
    this.setState({a: true}); // Doesn't re-render yet
    this.setState({b: true}); // Doesn't re-render yet
    this.props.setParentState(); // Doesn't re-render yet
  });
  // When we exit unstable_batchedUpdates, re-renders once
});

在unstable_batchedUpdates内部进行的setState会是异步调用,可是该API是不稳定的,由于后续的React版本更新中将会默认进行批处理即异步调用,届时该API将被删除。而这个后续的版本,极可能就是React 17

记录与分享,欢迎斧正,虚心求教

参考链接:
https://stackoverflow.com/que...
https://react.docschina.org/d...
https://github.com/Advanced-F...
https://github.com/sisterAn/b...

相关文章
相关标签/搜索