React中setState如何同步更新


一、說明

關於調用 setState() 進行狀態更新,官方有如下說明

State Updates May Be Asynchronous

React may batch multiple setState() calls into a single update for performance.

Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.

譯:

State 的更新可能是異步的

出於性能方面的考慮,React 可以將多次的 setState() 調用合並為一次

因為 this.props 和 this.state 可能是異步更新的,你不應該用它們當前的值去計算下一個 state 的值

在 React 的 setState 函數實現中,會根據一個變量 isBatchingUpdates 判斷是直接更新 this.state 還是放到隊列中延時更新,而 isBatchingUpdates 默認是 false,表示 setState 會同步更新 this.state;但是,有一個函數 batchedUpdates,該函數會把 isBatchingUpdates 修改為 true,而當 React 在調用事件處理函數之前就會先調用這個 batchedUpdates將isBatchingUpdates修改為true,這樣由 React 控制的事件處理過程 setState 不會同步更新 this.state。

二、setState何時同步何時異步?

由React控制的事件處理程序,以及生命周期函數調用setState不會同步更新state

React控制之外的事件中調用setState是同步更新的。比如原生js綁定的事件,setTimeout/setInterval等

大部分開發中用到的都是React封裝的事件,比如onChange、onClick、onTouchMove等,這些事件處理程序中的setState都是異步處理的。

三、同步更新策略

1.完成回調

setState函數的第二個參數允許傳入回調函數,在狀態更新完畢后進行調用,譬如:

this.setState({
      load: !this.state.load,
      count: this.state.count + 1
    }, () => {
      console.log(this.state.count);
      console.log('加載完成')
    });

2.Promise封裝結合async/await

其實這里就是JavaScript異步編程相關知識,將上面回調寫法換成Promise

引入Promise來封裝setState:

setStateAsync(state) {
    return new Promise((resolve) => {
      this.setState(state, resolve)
    });
  }

setStateAsync 返回的是Promise對象,在調用時我們可以使用Async/Await語法來優化代碼風格:

  async componentDidMount() {
       await this.setStateAsync ({
               count: this.state.count * 1 + 2
           })
           console.log(123, this.state.count)
           await this.setStateAsync ({
               count: this.state.count * 1 + 2
           })
           console.log(456, this.state.count)
  }

3.直接使用操作異步函數async / await 

此時每次await setState都會執行一遍render方法

handleChange = async () => {
           await this.setState({
               count: this.state.count * 1 + 2
           })
           console.log(123, this.state.count)
           await this.setState({
               count: this.state.count * 1 + 2
           })
           console.log(456, this.state.count)
 }

四、setState參數詳解

state中定義count初始值為1;

1.第一個參數為傳入對象;

handleChange = () => {
        this.setState({
            count: this.state.count  + 2
        })
        console.log(123, this.state.count)
        this.setState({
            count: this.state.count  + 2
        })
        console.log(456, this.state.count)
    }

調用方法兩次輸出值為都為3;

 React 出於性能方面的考慮,並不會直接對每次的調用都進行更新,而是會將多次傳入的對象進行合並處理,以產生一個新的最終的 state 對象;

第二個參數為完成回調

handleChange = () => {
        this.setState({
            count: this.state.count  + 2
        }, () => {
            console.log(123, this.state.count)
        })
        this.setState({
            count: this.state.count  + 2
        }, () => {
            console.log(456, this.state.count)
        })
    }

與上面輸出保持一致

2.第一個參數為傳入函數:

handleChange = () => {
        this.setState((prevState, props) => ({
            count: prevState.count  + 2
        }), () => {
            console.log(123, this.state.count)
        })
        this.setState((prevState, props) => ({
            count: prevState.count  + 2
        }), () => {
            console.log(456, this.state.count)
        })
    }

函數第一個參數接收先前的狀態作為第一個參數,將此次更新被應用時的props做為第二個參數;

此時輸出為兩次都是5;

在第二次調用 setState 方法時便可以通過 prevState.count 拿到最新的值從而更新本次的 state 。顯然,React 對於傳入函數的方式和傳入對象的方式進行更新 state 的各自具體理念是不一樣的,對於傳入函數的方式,在調用 setState 進行更新 state 時,React 會按照各個 setState 的調用順序,將它們依次放入一個隊列,然后,在進行狀態更新時,則按照隊列中的先后順序依次調用,並將上一個調用結束時產生的 state 傳入到下一個調用的函數中,當然,第一個 setState 調用時,傳入的 prevState 則是當前的 state ,如此,便解決了傳入對象式調用 setState 方法所存在的 不能依賴上一次的 state 去計算本次 state 的問題。

handleChange = () => {
        this.setState((prevState, props) => ({
            count: prevState.count  + 2
        }), () => {
            console.log(123, this.state.count)
        })
        this.setState((prevState, props) => ({
            count: this.state.count  + 2
        }), () => {
            console.log(456, this.state.count)
        })
    }

值得一提的是,在上面的這段代碼中,執行第二個 setState 里面的函數時,由第一個 setState 所產生的最新的 state 並沒有合並到 this 對象上面去,所以此時通過 this.state 獲取不到最新的狀態,故而在函數內部拿到的 this.state.count 的值為 1 而非 3。

另外,不論第一個參數是何種類型,多次setState也只會調用一次render方法,第二個參數callback完成回調中獲取this.state的值同樣也是多次setState合並操作后的最終值;

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM