React中setState什麼時候是同步的,什麼時候是異步的?

React 中 setState 什麼時候是"同步"的,什麼時候是"異步"的?

在React中,如果是由React引發的事件處理(比如通過onClick引發的事件處理),調用setState不會同步更新this.state,除此之外的setState調用會同步執行this.state 。所謂“除此之外”,指的是繞過React通過addEventListener直接添加的事件處理函數,還有通過setTimeout/setInterval產生的異步調用。

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

原因

「原因」: 在React的setState函數實現中,會根據一個變量isBatchingUpdates判斷是直接更新this.state還是放到隊列中回頭再說,而isBatchingUpdates默認是false,也就表示setState會同步更新this.state,但是,有一個函數batchedUpdates,這個函數會把isBatchingUpdates修改爲true,而當React在調用事件處理函數之前就會調用這個batchedUpdates,造成的後果,就是由React控制的事件處理過程setState不會同步更新this.state。

注意

注意: setState的“異步”並不是說內部由異步代碼實現,其實本身執行的過程和代碼都是同步的,只是合成事件和鉤子函數的調用順序在更新之前,導致在合成事件和鉤子函數中沒法立馬拿到更新後的值,形式了所謂的“異步”,當然可以通過第二個參數 setState(partialState, callback) 中的callback拿到更新後的結果。

React setState 筆試題,下面的代碼輸出什麼?

class Example extends React.Component {
  constructor() {
    super();
    this.state = {
      val: 0
    };
  }
  
  componentDidMount() {
    this.setState({val: this.state.val + 1});
    console.log(this.state.val);    // 第 1 次 log

    this.setState({val: this.state.val + 1});
    console.log(this.state.val);    // 第 2 次 log

    setTimeout(() => {
      this.setState({val: this.state.val + 1});
      console.log(this.state.val);  // 第 3 次 log

      this.setState({val: this.state.val + 1});
      console.log(this.state.val);  // 第 4 次 log
    }, 0);
  }

  render() {
    return null;
  }
};
  • 如果有這種 async 的 work 就不執行 batch update 如果沒有 async 的就執行 batch update,setTimeout 和 promise 這些要進入 EventLoop 隊列的都會被認爲是 async work。

  1. 第一次和第二次都是在 react 自身生命週期內,觸發時 isBatchingUpdates 爲 true,所以並不會直接執行更新 state,而是加入了 dirtyComponents,所以打印時獲取的都是更新前的狀態 0。

  2. 兩次 setState 時,獲取到 this.state.val 都是 0,所以執行時都是將 0 設置成 1,在 react 內部會被合併掉,只執行一次。設置完成後 state.val 值爲 1。

  3. setTimeout 中的代碼,觸發時 isBatchingUpdates 爲 false,所以能夠直接進行更新,所以連着輸出 2,3。

「輸出: 0 0 2 3」

本文使用 mdnice 排版

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章