天天看点

setState为什么是异步更新

1.)setState操作是异步的,并且为了性能提升而进行批处理。这在setState的文档中有解释。

setState()不会立即改变this.state,但会创建挂起状态转换。调用此方法后访问this.state可能会返回现有值。无法保证对setState的调用进行同步操作,并且可以对调用进行批处理以提高性能。

2.)为什么它们会使setState异步,因为JS是单线程语言,并且此setState不是WebAPI或服务器调用:

这是因为setState改变了状态并导致重新渲染。这可能是一项昂贵的操作,并使其同步可能会使浏览器无响应。 

因此,setState调用是异步的以及批处理以获得更好的UI体验和性能。

​setState​

​意图是异步的,在Dan Abramov的链接解释中有一些非常好的理由。这并不意味着它总是异步的 - 它主要意味着你不能依赖它是同步的。ReactJS考虑了您正在更改状态的场景中的许多变量,以确定何时​

​state​

​​应该实际更新并重新呈现组件。

一个简单的例子来说明,如果你打电话​​

​setState​

​​作为对用户操作的反应,那么​

​state​

​​可能会立即更新(虽然,你再也不能指望它),所以用户不会感到任何延迟,但如果你打电话​

​setState​

​ 为了响应ajax调用响应或其他一些不是由用户触发的事件,那么状态可能会稍微延迟更新,因为用户不会真正感受到这种延迟,并且它会通过等待来提高性能批处理多个状态一起更新并重新渲染DOM的次数更少

想象一下在某个组件中递增计数器:

class SomeComponent extends Component{

    state = {
      updatedByDiv: '',
      updatedByBtn: '',
      counter: 0
    }

    divCountHandler = () => {
      this.setState({
        updatedByDiv: 'Div',
        counter: this.state.counter + 1
      });
      console.log('divCountHandler executed');
    }

    btnCountHandler = () => {
      this.setState({
        updatedByBtn: 'Button',
        counter: this.state.counter + 1
      });
      console.log('btnCountHandler executed');
    }
    ...
    ...
    render(){
      return (
        ...
        // a parent div
        <div onClick={this.divCountHandler}>
          // a child button
          <button onClick={this.btnCountHandler}>Increment Count</button>
        </div>
        ...
      )
    }
  }      

有一个计数处理程序附加到父组件和子组件。这是故意完成的,因此我们可以在相同的单击事件冒泡上下文中执行两次setState(),但是从2个不同的处理程序中执行。

正如我们想象的那样,按钮上的单击事件现在将触发这两个处理程序,因为事件在冒泡阶段从目标气泡到最外面的容器。

因此,btnCountHandler()首先执行,期望将计数增加到1然后执行divCountHandler(),期望将计数增加到2。

但是,计数只会增加到1,因为您可以在React Developer工具中进行检查。

这证明了反应

  • 排队所有setState调用
  • 在上下文中执行最后一个方法后回到此队列(在本例中为divCountHandler)
  • 将在同一上下文中的多个setState调用内发生的所有对象变换(例如,单个事件阶段中的所有方法调用都是相同的上下文)合并为一个单个对象变异语法(合并是有意义的,因为这就是我们可以独立更新状态属性的原因在setState()中首先)
  • 并将其传递给一个单独的setState()以防止由于多个setState()调用而重新呈现(这是一个非常原始的批处理描述)。

反应运行的结果代码:

this.setState({
  updatedByDiv: 'Div',
  updatedByBtn: 'Button',
  counter: this.state.counter + 1
})      

要停止此行为,不要将对象作为参数传递给setState方法,而是传递回调。

divCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByDiv: 'Div',
              counter: prevState.counter + 1
            };
          });
          console.log('divCountHandler executed');
        }

    btnCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByBtn: 'Button',
              counter: prevState.counter + 1
            };
          });
      console.log('btnCountHandler executed');
    }