天天看點

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');
    }