为什么说setState方法是异步的?

在学习或使用过一阵子React后,你可能会发现一个在setState方法的特性,如下面这个简单例子来讲明:html

export default class SelectBox extends React.Component {
  constructor(props) {
    super(props)
    this.state={value: ''}
  }

  handleChange = (e) => {
    this.setState({value: e.target.value})
    console.log(this.state.value)
  }

  render() {
      return(
         <div>
             <select onChange={this.handleChange} value={this.state.value}>
                <option value="JavaScript" key={1}>JavaScript</option>
                <option value="Angular2" key={2}>Angular2</option>
                <option value="React" key={3}>React</option>
             </select>
             <h1>{this.state.value}</h1>
         </div>
      )
   }
}

咱们在handleChange方法中,呼叫setState来更新选项的值,而后在控制台中输出这个值。看起来一切都是很符合逻辑,但你若是一执行就会发现,在控制台中输出的this.state.value,并不会在呼叫setState方法后当即就变更。像下面的执行的结果图同样:react

执行结果

固然,若是你直接输出的是e.target.value,必定是正确的值,但在某些状况下,咱们要取用的并非这个事件的值,而是要更动事后的state(状态)值。git

若是要在setState方法后,直接取用更动后的state值,正确的使用方式,在官方文件中的说明,须要利用setState的第二传参,传入一个回调(callback)函式,改成像下面这样的代码:github

this.setState({value: e.target.value}, function(){ console.log(this.state.value) })

另外一个方式则是用componentDidUpdate()这个生命周期方法,把肯定state更新后要执行的代码放在里面,以下面的代码:算法

componentDidUpdate(){
  console.log(this.state.value)
}

为何必定要这样做的主要缘由是:promise

setState这个方法,它在React中的执行行为能够认为"异步的"异步

虽然setState并不是使用了setTimeout或promise的那种进入到事件回圈(Event loop)的异步执行,但它的执行行为在React库中时,的确是异步的,也就是有延时执行的行为。以官方文件中较精确的说法 - "它不是保证同步的"。async

setState方法与包含在其中的执行是一个很复杂的过程,这段程式码从React最初的版本到如今,也有无数次的修改。它的工做除了要更动this.state以外,还要负责触发从新渲染(render),这里面要通过React核心中diff演算法,最终才能决定是否要进行重渲染,以及如何渲染。并且为了批次与效能的理由,多个setState呼叫有可能在执行过程当中还须要被合并,因此它被设计以异步的或延时的来进行执行是至关合理的。oop

那么setState会在什么时候以同步的方式来执行,也就是当即更动this.state?答案是在React库控制以外时,它就会以同步的方式来执行,在下面两篇文章中,都有相似的例子:学习

但大部份的使用状况下,咱们都是使用了React库中的表单组件,例如select、input、button等等,它们都是React库中人造的组件与事件,是处于React库的控制之下,在这个状况下,setState就会以异步的方式执行。因此通常来讲,咱们会认为setState就是异步执行,并非用原始码来看它是否是有使用像setTimeoutPromise之类的方式转为JavaScript的异步执行方式,而是以它在React库的控制之下,以执行行为与顺序来认定。

如下是翻自官方setState原代码的注解,官网的说明也是相似:

不保证this.state会当即更新,因此在调用这个方法后存取this.state可能会回传旧的值。

不保证呼叫setState就会同步地执行,而它们也可能最终被被批量调用(屡次呼叫的状况下)。你能够提供额外的回调(callback),回调(callback)将会在setState实际被完成时被执行。

所以,很早就有开发者提出来关于setState常令初学者感到怪异的执行状况,在某些状况下会形成执行后会看到不连续的结果。除了setState方法有异步执行的行为外,它还有几个被提出来的特殊行为:

1. setState可能会引起没必要要的渲染(renders)

state自己的设计是没法直接更改,setState的设计是用来更动state值,也会触发从新渲染(re-render),按照逻辑就是反正无论如何,只要开发者呼叫setState,React就去做整个视图的从新渲染就是。因此setState一定会做从新渲染的执行,只是要如何渲染是由React来决定。

从新渲染(re-render)指的主要是页面上视图(View)的从新再呈现,这是React本来的核心设计,但这个设计是有一些问题的。最主要的是state(状态)并不必定单纯只用来记录与视图(View)有关的状态,也有多是某个内部控制用的属性值,或是只套用在内部使用的资料。当你改变了这些与视图无关的state(状态)值,以如今的React设计来讲,照样要触发从新渲染的执行过程,这在某些复杂的应用时,因为形成没必要要的渲染,也有可能形成效能上的问题。

固然,React提供了shouldComponentUpdate方法让开发者能够自行判断,自行提供对应的解决方式。也有Performance Tools能够进行剖析检测。算得上是一些补强的做法。

2. setState没法彻底掌控应用中全部组件的状态

state(状态)是独立于每一个组件内部的,并且它是个不能直接更动的对象,这个设计固然是为了要保持组件的封装与独立性,但因此若是当要开发一个复杂的应用时,一定须要使用那些能掌控全部组件资料,以及能提供各组件间资料互动的函式库,例如Flux, Redux或MobX等等。

React组件目前只能透过各类生命周期的方法,与外部资源、计时器或DOM事件来进行挂勾(Hook),这些都没法直接使用setState方法来进行管理,所以setState并无办法彻底掌控一个应用中全部组件的状态,它比较像是每一个组件中的都有的一种接口方法,单纯要依靠setState方法来管控整个React应用,彻底是不足够的。

以上说明参考自这篇文章: 3 Reasons why I stopped using React.setState

相关文章
相关标签/搜索