react源碼解析12.狀態(tài)更新流程

react源碼解析12.狀態(tài)更新流程

視頻課程(高效學(xué)習(xí)):進(jìn)入課程

課程目錄:

1.開(kāi)篇介紹和面試題

2.react的設(shè)計(jì)理念

3.react源碼架構(gòu)

4.源碼目錄結(jié)構(gòu)和調(diào)試

5.jsx&核心api

6.legacy和concurrent模式入口函數(shù)

7.Fiber架構(gòu)

8.render階段

9.diff算法

10.commit階段

11.生命周期

12.狀態(tài)更新流程

13.hooks源碼

14.手寫(xiě)hooks

15.scheduler&Lane

16.concurrent模式

17.context

18事件系統(tǒng)

19.手寫(xiě)迷你版react

20.總結(jié)&第一章的面試題解答

21.demo

setState&forceUpdate

在react中觸發(fā)狀態(tài)更新的幾種方式:

  • ReactDOM.render
  • this.setState
  • this.forceUpdate
  • useState
  • useReducer

我們重點(diǎn)看下重點(diǎn)看下this.setState和this.forceUpdate,hook在第13章講

  1. this.setState內(nèi)調(diào)用this.updater.enqueueSetState,主要是將update加入updateQueue中

    //ReactBaseClasses.js
    Component.prototype.setState = function (partialState, callback) {
      if (!(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null)) {
        {
          throw Error( "setState(...): takes an object of state variables to update or a function which returns an object of state variables." );
        }
      }
      this.updater.enqueueSetState(this, partialState, callback, 'setState');
    };
    
    
//ReactFiberClassComponent.old.js
enqueueSetState(inst, payload, callback) {
  const fiber = getInstance(inst);//fiber實(shí)例

  const eventTime = requestEventTime();
  const suspenseConfig = requestCurrentSuspenseConfig();
  
  const lane = requestUpdateLane(fiber, suspenseConfig);//優(yōu)先級(jí)

  const update = createUpdate(eventTime, lane, suspenseConfig);//創(chuàng)建update

  update.payload = payload;

  if (callback !== undefined && callback !== null) {  //賦值回調(diào)
    update.callback = callback;
  }

  enqueueUpdate(fiber, update);//update加入updateQueue
  scheduleUpdateOnFiber(fiber, lane, eventTime);//調(diào)度update
}

enqueueUpdate用來(lái)將update加入updateQueue隊(duì)列

//ReactUpdateQueue.old.js
export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
  const updateQueue = fiber.updateQueue;
  if (updateQueue === null) {
    return;
  }

  const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
  const pending = sharedQueue.pending;
  if (pending === null) {
    update.next = update;//與自己形成環(huán)狀鏈表
  } else {
    update.next = pending.next;//加入鏈表的結(jié)尾
    pending.next = update;
  }
  sharedQueue.pending = update;
}
react源碼12.6
  1. this.forceUpdate和this.setState一樣,只是會(huì)讓tag賦值ForceUpdate

    //ReactBaseClasses.js
    Component.prototype.forceUpdate = function(callback) {
      this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
    };
    
    
//ReactFiberClassComponent.old.js
enqueueForceUpdate(inst, callback) {
    const fiber = getInstance(inst);
    const eventTime = requestEventTime();
    const suspenseConfig = requestCurrentSuspenseConfig();
    const lane = requestUpdateLane(fiber, suspenseConfig);

    const update = createUpdate(eventTime, lane, suspenseConfig);
    
    //tag賦值ForceUpdate
    update.tag = ForceUpdate;
    
    if (callback !== undefined && callback !== null) {
      update.callback = callback;
    }
    
    enqueueUpdate(fiber, update);
    scheduleUpdateOnFiber(fiber, lane, eventTime);

  },
};
如果標(biāo)記ForceUpdate,render階段組件更新會(huì)根據(jù)checkHasForceUpdateAfterProcessing,和checkShouldComponentUpdate來(lái)判斷,如果Update的tag是ForceUpdate,則checkHasForceUpdateAfterProcessing為true,當(dāng)組件是PureComponent時(shí),checkShouldComponentUpdate會(huì)淺比較state和props,所以當(dāng)使用this.forceUpdate一定會(huì)更新
//ReactFiberClassComponent.old.js
const shouldUpdate =
  checkHasForceUpdateAfterProcessing() ||
  checkShouldComponentUpdate(
    workInProgress,
    ctor,
    oldProps,
    newProps,
    oldState,
    newState,
    nextContext,
  );

狀態(tài)更新整體流程

react源碼12.1

Update&updateQueue

HostRoot或者ClassComponent觸發(fā)更新后,會(huì)在函數(shù)createUpdate中創(chuàng)建update,并在后面的render階段的beginWork中計(jì)算Update。FunctionComponent對(duì)應(yīng)的Update在第11章講,它和HostRoot或者ClassComponent的Update結(jié)構(gòu)有些不一樣

//ReactUpdateQueue.old.js
export function createUpdate(eventTime: number, lane: Lane): Update<*> {//創(chuàng)建update
  const update: Update<*> = {
    eventTime,
    lane,

    tag: UpdateState,
    payload: null,
    callback: null,

    next: null,
  };
  return update;
}

我們主要關(guān)注這些參數(shù):

  • lane:優(yōu)先級(jí)(第12章講)

  • tag:更新的類型,例如UpdateState、ReplaceState

  • payload:ClassComponent的payload是setState第一個(gè)參數(shù),HostRoot的payload是ReactDOM.render的第一個(gè)參數(shù)

  • callback:setState的第二個(gè)參數(shù)

  • next:連接下一個(gè)Update形成一個(gè)鏈表,例如同時(shí)觸發(fā)多個(gè)setState時(shí)會(huì)形成多個(gè)Update,然后用next 連接

對(duì)于HostRoot或者ClassComponent會(huì)在mount的時(shí)候使用initializeUpdateQueue創(chuàng)建updateQueue,然后將updateQueue掛載到fiber節(jié)點(diǎn)上

//ReactUpdateQueue.old.js
export function initializeUpdateQueue<State>(fiber: Fiber): void {
  const queue: UpdateQueue<State> = {
    baseState: fiber.memoizedState,
    firstBaseUpdate: null,
    lastBaseUpdate: null,
  shared: {
      pending: null,
    },
    effects: null,
  };
fiber.updateQueue = queue;
}
  • baseState:初始state,后面會(huì)基于這個(gè)state,根據(jù)Update計(jì)算新的state
  • firstBaseUpdate、lastBaseUpdate:Update形成的鏈表的頭和尾
  • shared.pending:新產(chǎn)生的update會(huì)以單向環(huán)狀鏈表保存在shared.pending上,計(jì)算state的時(shí)候會(huì)剪開(kāi)這個(gè)環(huán)狀鏈表,并且鏈接在lastBaseUpdate后
  • effects:calback不為null的update

從觸發(fā)更新的fiber節(jié)點(diǎn)向上遍歷到rootFiber

在markUpdateLaneFromFiberToRoot函數(shù)中會(huì)從觸發(fā)更新的節(jié)點(diǎn)開(kāi)始向上遍歷到rootFiber,遍歷的過(guò)程會(huì)處理節(jié)點(diǎn)的優(yōu)先級(jí)(第15章講)

//ReactFiberWorkLoop.old.js
function markUpdateLaneFromFiberToRoot(
    sourceFiber: Fiber,
    lane: Lane,
  ): FiberRoot | null {
    sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
    let alternate = sourceFiber.alternate;
    if (alternate !== null) {
      alternate.lanes = mergeLanes(alternate.lanes, lane);
    }
    let node = sourceFiber;
    let parent = sourceFiber.return;
    while (parent !== null) {//從觸發(fā)更新的節(jié)點(diǎn)開(kāi)始向上遍歷到rootFiber
      parent.childLanes = mergeLanes(parent.childLanes, lane);//合并childLanes優(yōu)先級(jí)
      alternate = parent.alternate;
      if (alternate !== null) {
        alternate.childLanes = mergeLanes(alternate.childLanes, lane);
      } else {
      }
      node = parent;
      parent = parent.return;
    }
    if (node.tag === HostRoot) {
      const root: FiberRoot = node.stateNode;
      return root;
    } else {
      return null;
    }
  }

例如B節(jié)點(diǎn)觸發(fā)更新,B節(jié)點(diǎn)被被標(biāo)記為normal的update,也就是圖中的u1,然后向上遍歷到根節(jié)點(diǎn),在根節(jié)點(diǎn)上打上一個(gè)normal的update,如果此時(shí)B節(jié)點(diǎn)又觸發(fā)了一個(gè)userBlocking的Update,同樣會(huì)向上遍歷到根節(jié)點(diǎn),在根節(jié)點(diǎn)上打上一個(gè)userBlocking的update。

如果當(dāng)前根節(jié)點(diǎn)更新的優(yōu)先級(jí)是normal,u1、u2都參與狀態(tài)的計(jì)算,如果當(dāng)前根節(jié)點(diǎn)更新的優(yōu)先級(jí)是userBlocking,則只有u2參與計(jì)算

react源碼12.5

調(diào)度

在ensureRootIsScheduled中,scheduleCallback會(huì)以一個(gè)優(yōu)先級(jí)調(diào)度render階段的開(kāi)始函數(shù)performSyncWorkOnRoot或者performConcurrentWorkOnRoot

//ReactFiberWorkLoop.old.js
if (newCallbackPriority === SyncLanePriority) {
  // 任務(wù)已經(jīng)過(guò)期,需要同步執(zhí)行render階段
  newCallbackNode = scheduleSyncCallback(
    performSyncWorkOnRoot.bind(null, root)
  );
} else {
  // 根據(jù)任務(wù)優(yōu)先級(jí)異步執(zhí)行render階段
  var schedulerPriorityLevel = lanePriorityToSchedulerPriority(
    newCallbackPriority
  );
  newCallbackNode = scheduleCallback(
    schedulerPriorityLevel,
    performConcurrentWorkOnRoot.bind(null, root)
  );
}

狀態(tài)更新

classComponent狀態(tài)計(jì)算發(fā)生在processUpdateQueue函數(shù)中,涉及很多鏈表操作,看圖更加直白

  • 初始時(shí)fiber.updateQueue單鏈表上有firstBaseUpdate(update1)和lastBaseUpdate(update2),以next連接

  • fiber.updateQueue.shared環(huán)狀鏈表上有update3和update4,以next連接互相連接

  • 計(jì)算state時(shí),先將fiber.updateQueue.shared環(huán)狀鏈表‘剪開(kāi)’,形成單鏈表,連接在fiber.updateQueue后面形成baseUpdate

  • 然后遍歷按這條鏈表,根據(jù)baseState計(jì)算出memoizedState

    react源碼12.2

帶優(yōu)先級(jí)的狀態(tài)更新

類似git提交,這里的c3意味著高優(yōu)先級(jí)的任務(wù),比如用戶出發(fā)的事件,數(shù)據(jù)請(qǐng)求,同步執(zhí)行的代碼等。

  • 通過(guò)ReactDOM.render創(chuàng)建的應(yīng)用沒(méi)有優(yōu)先級(jí)的概念,類比git提交,相當(dāng)于先commit,然后提交c3


    react源碼12.3
  • 在concurrent模式下,類似git rebase,先暫存之前的代碼,在master上開(kāi)發(fā),然后rebase到之前的分支上

    優(yōu)先級(jí)是由Scheduler來(lái)調(diào)度的,這里我們只關(guān)心狀態(tài)計(jì)算時(shí)的優(yōu)先級(jí)排序,也就是在函數(shù)processUpdateQueue中發(fā)生的計(jì)算,例如初始時(shí)有c1-c4四個(gè)update,其中c1和c3為高優(yōu)先級(jí)

    1. 在第一次render的時(shí)候,低優(yōu)先級(jí)的update會(huì)跳過(guò),所以只有c1和c3加入狀態(tài)的計(jì)算
    2. 在第二次render的時(shí)候,會(huì)以第一次中跳過(guò)的update(c2)之前的update(c1)作為baseState,跳過(guò)的update和之后的update(c2,c3,c4)作為baseUpdate重新計(jì)算

    在在concurrent模式下,componentWillMount可能會(huì)執(zhí)行多次,變現(xiàn)和之前的版本不一致

    注意,fiber.updateQueue.shared會(huì)同時(shí)存在于workInprogress Fiber和current Fiber,目的是為了防止高優(yōu)先級(jí)打斷正在進(jìn)行的計(jì)算而導(dǎo)致?tīng)顟B(tài)丟失,這段代碼也是發(fā)生在processUpdateQueue中

react源碼12.4

看demo_8的優(yōu)先級(jí)

現(xiàn)在來(lái)看下計(jì)算狀態(tài)的函數(shù)

//ReactUpdateQueue.old.js
export function processUpdateQueue<State>(
  workInProgress: Fiber,
  props: any,
  instance: any,
  renderLanes: Lanes,
): void {
  const queue: UpdateQueue<State> = (workInProgress.updateQueue: any);
  hasForceUpdate = false;

  let firstBaseUpdate = queue.firstBaseUpdate;//updateQueue的第一個(gè)Update
  let lastBaseUpdate = queue.lastBaseUpdate;//updateQueue的最后一個(gè)Update
  let pendingQueue = queue.shared.pending;//未計(jì)算的pendingQueue

  if (pendingQueue !== null) {
    queue.shared.pending = null;
    const lastPendingUpdate = pendingQueue;//未計(jì)算的ppendingQueue的最后一個(gè)update
    const firstPendingUpdate = lastPendingUpdate.next;//未計(jì)算的pendingQueue的第一個(gè)update
    lastPendingUpdate.next = null;//剪開(kāi)環(huán)狀鏈表
    if (lastBaseUpdate === null) {//將pendingQueue加入到updateQueue
      firstBaseUpdate = firstPendingUpdate;
    } else {
      lastBaseUpdate.next = firstPendingUpdate;
    }
    lastBaseUpdate = lastPendingUpdate;

    const current = workInProgress.alternate;//current上做同樣的操作
    if (current !== null) {
      const currentQueue: UpdateQueue<State> = (current.updateQueue: any);
      const currentLastBaseUpdate = currentQueue.lastBaseUpdate;
      if (currentLastBaseUpdate !== lastBaseUpdate) {
        if (currentLastBaseUpdate === null) {
          currentQueue.firstBaseUpdate = firstPendingUpdate;
        } else {
          currentLastBaseUpdate.next = firstPendingUpdate;
        }
        currentQueue.lastBaseUpdate = lastPendingUpdate;
      }
    }
  }

  if (firstBaseUpdate !== null) {
    let newState = queue.baseState;

    let newLanes = NoLanes;

    let newBaseState = null;
    let newFirstBaseUpdate = null;
    let newLastBaseUpdate = null;

    let update = firstBaseUpdate;
    do {
      const updateLane = update.lane;
      const updateEventTime = update.eventTime;
      if (!isSubsetOfLanes(renderLanes, updateLane)) {//判斷優(yōu)先級(jí)是夠足夠
        const clone: Update<State> = {//優(yōu)先級(jí)不夠 跳過(guò)當(dāng)前update
          eventTime: updateEventTime,
          lane: updateLane,

          tag: update.tag,
          payload: update.payload,
          callback: update.callback,

          next: null,
        };
        if (newLastBaseUpdate === null) {//保存跳過(guò)的update
          newFirstBaseUpdate = newLastBaseUpdate = clone;
          newBaseState = newState;
        } else {
          newLastBaseUpdate = newLastBaseUpdate.next = clone;
        }
        newLanes = mergeLanes(newLanes, updateLane);
      } else {
        //直到newLastBaseUpdate為null才不會(huì)計(jì)算,防止updateQueue沒(méi)計(jì)算完
        if (newLastBaseUpdate !== null) {
          const clone: Update<State> = {
            eventTime: updateEventTime,
            lane: NoLane,

            tag: update.tag,
            payload: update.payload,
            callback: update.callback,

            next: null,
          };
          newLastBaseUpdate = newLastBaseUpdate.next = clone;
        }

        newState = getStateFromUpdate(//根據(jù)updateQueue計(jì)算state
          workInProgress,
          queue,
          update,
          newState,
          props,
          instance,
        );
        const callback = update.callback;
        if (callback !== null) {
          workInProgress.flags |= Callback;//Callback flag
          const effects = queue.effects;
          if (effects === null) {
            queue.effects = [update];
          } else {
            effects.push(update);
          }
        }
      }
      update = update.next;//下一個(gè)update
      if (update === null) {//重置updateQueue
        pendingQueue = queue.shared.pending;
        if (pendingQueue === null) {
          break;
        } else {
          const lastPendingUpdate = pendingQueue;

          const firstPendingUpdate = ((lastPendingUpdate.next: any): Update<State>);
          lastPendingUpdate.next = null;
          update = firstPendingUpdate;
          queue.lastBaseUpdate = lastPendingUpdate;
          queue.shared.pending = null;
        }
      }
    } while (true);

    if (newLastBaseUpdate === null) {
      newBaseState = newState;
    }

    queue.baseState = ((newBaseState: any): State);//新的state
    queue.firstBaseUpdate = newFirstBaseUpdate;//新的第一個(gè)update
    queue.lastBaseUpdate = newLastBaseUpdate;//新的最后一個(gè)update

    markSkippedUpdateLanes(newLanes);
    workInProgress.lanes = newLanes;
    workInProgress.memoizedState = newState;
  }

    //...
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容