React源碼解析之setState和forceUpdate

1、enqueueSetState()
非異步方法中,不管調用多少個setState,它們都會在最後一次setState後,放入更新隊列,而後執行一次統一的更新,詳情請參考:
React.setState之state批處理的機制爲何React.setState是異步的? javascript

做用:
React節點的fiber對象建立update,並將該更新對象入隊java

源碼:react

//classComponent初始化的時候拿到的update對象
const classComponentUpdater = {
  isMounted,
  enqueueSetState(inst, payload, callback) {
    //inst即調用this.setState時傳進來的this
    //也就是classComponent實例

    //經過this獲取fiber對象
    //this._reactInternalFiber
    //this自己有存儲 fiber對象 的屬性,叫 _reactInternalFiber
    const fiber = getInstance(inst);
    //計算當前時間,以前講過 不講了
    const currentTime = requestCurrentTime();
    //異步加載的設置,暫時不講
    const suspenseConfig = requestCurrentSuspenseConfig();
    //計算fiber對象的過時時間
    const expirationTime = computeExpirationForFiber(
      currentTime,
      fiber,
      suspenseConfig,
    );
    //建立update對象
    const update = createUpdate(expirationTime, suspenseConfig);
    //setState傳進來的要更新的對象
    update.payload = payload;
    //callback就是setState({},()=>{})的回調函數
    if (callback !== undefined && callback !== null) {
      if (__DEV__) {
        warnOnInvalidCallback(callback, 'setState');
      }
      update.callback = callback;
    }
    //暫時無論
    if (revertPassiveEffectsChange) {
      flushPassiveEffects();
    }
    //update入隊
    enqueueUpdate(fiber, update);
    //任務調度
    scheduleWork(fiber, expirationTime);
  },
};
複製代碼

解析:
(1)getInstanceweb

//getInstance
export function get(key{
  return key._reactInternalFiber;
}
複製代碼

就是獲取目標對象的_reactInternalFiber屬性,即this.setState中的thisapp

(2)requestCurrentTime,請見:React源碼解析之ReactDOM.render()異步

(3)computeExpirationForFiber,請見:React源碼解析之ExpirationTime函數

(4)createUpdate,請見:React源碼解析之Update和UpdateQueuepost

(5)注意下payloadpayload就是setState傳進來的要更新的對象this

this.setState({a:1},callback) 中的 {a:1} 即 payload
//====================
update.payload = payload;
複製代碼

(6)enqueueUpdate,請見:React源碼解析之Update和UpdateQueuespa

(7)scheduleWork,篇幅較長,會放在下篇講。

2、enqueueForceUpdate()
做用:
強制讓組件從新渲染,也是給React節點的fiber對象建立update,並將該更新對象入隊

源碼:

  enqueueForceUpdate(inst, callback) {
    const fiber = getInstance(inst);
    const currentTime = requestCurrentTime();
    const suspenseConfig = requestCurrentSuspenseConfig();
    const expirationTime = computeExpirationForFiber(
      currentTime,
      fiber,
      suspenseConfig,
    );

    const update = createUpdate(expirationTime, suspenseConfig);
    //與setState不一樣的地方
    //默認是0更新,須要改爲2強制更新
    update.tag = ForceUpdate;

    if (callback !== undefined && callback !== null) {
      if (__DEV__) {
        warnOnInvalidCallback(callback, 'forceUpdate');
      }
      update.callback = callback;
    }

    if (revertPassiveEffectsChange) {
      flushPassiveEffects();
    }
    enqueueUpdate(fiber, update);
    scheduleWork(fiber, expirationTime);
  },
複製代碼

解析:
enqueueSetState()方法的流程相似,惟一不一樣的是多了個手動修改屬性tag的值:

//與setState不一樣的地方
//默認是0更新,須要改爲2強制更新
update.tag = ForceUpdate;
複製代碼

能夠看到createUpdate()方法中,初始化的tag值是UpdateState

//建立update對象
export function createUpdate(
  expirationTime: ExpirationTime,
  suspenseConfig: null | SuspenseConfig,
): Update<*> 
{
  return {
    // export const UpdateState = 0;
    // export const ReplaceState = 1;
    // export const ForceUpdate = 2;
    // export const CaptureUpdate = 3;

    //重點提下CaptureUpdate,在React16後有一個ErrorBoundaries功能
    //即在渲染過程當中報錯了,能夠選擇新的渲染狀態(提示有錯誤的狀態),來更新頁面
    //默認是0即更新
    tag: UpdateState, //0更新 1替換 2強制更新 3捕獲性的更新
  };
}
複製代碼

所以要改爲ForceUpdate,以便React進行Update優先級排序

3、綜上
執行setStateforUpdateReact進行更新的流程爲:
(1)獲取this上的fiber對象
(2)計算currentTime
(3)根據(1)fiber(2)currentTime計算fiber對象的expirationTime
(4)根據(3)expirationTime建立update對象
(5)將setState中要更新的對象賦值到(4)update.payload
(6)將setState中要執行的callback賦值到(4)update.callback
(7)update入隊updateQueue
(8)進行任務調度


(完)

相關文章
相關標籤/搜索