最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React源碼state計(jì)算流程和優(yōu)先級(jí)實(shí)例解析

 更新時(shí)間:2022年11月08日 11:48:51   作者:goClient1992  
這篇文章主要為大家介紹了React源碼state計(jì)算流程和優(yōu)先級(jí)實(shí)例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

setState執(zhí)行之后會(huì)發(fā)生什么

setState 執(zhí)行之后,會(huì)執(zhí)行一個(gè)叫 enqueueSetState 的方法,這個(gè)主要作用是創(chuàng)建 Update 對(duì)象和發(fā)起調(diào)度,可以看下這個(gè)函數(shù)的邏輯,

enqueueSetState: function (inst, payload, callback) {
    // 1. inst是組件實(shí)例,從組件實(shí)例中拿到當(dāng)前組件的Fiber節(jié)點(diǎn)
    var fiber = get(inst);
    var eventTime = requestEventTime();
    var lane = requestUpdateLane(fiber);
    // 2.1 根據(jù)更新發(fā)起時(shí)間、優(yōu)先級(jí)、更新的payload創(chuàng)建一個(gè)update對(duì)象
    var update = createUpdate(eventTime, lane);
    update.payload = payload;
    // 2.2 如果 setState 有回調(diào),順便把回調(diào)賦值給 update 對(duì)象的 callback 屬性
    if (callback !== undefined && callback !== null) {
      update.callback = callback;
    }
    // 3. 將 update 對(duì)象關(guān)聯(lián)到 Fiber 節(jié)點(diǎn)的 updateQueue 屬性中
    enqueueUpdate(fiber, update);
    // 4. 發(fā)起調(diào)度
    var root = scheduleUpdateOnFiber(fiber, lane, eventTime);
}

從上面源碼可以清晰知道,setState 調(diào)用之后做的4件事情

  • 根據(jù)組件實(shí)例獲取其 Fiber 節(jié)點(diǎn)
  • 創(chuàng)建 Update 對(duì)象
  • Update 對(duì)象關(guān)聯(lián)到 Fiber 節(jié)點(diǎn)的 updateQueue 屬性中
  • 發(fā)起調(diào)度

根據(jù)組件實(shí)例獲取其 Fiber 節(jié)點(diǎn)

其實(shí)就是拿組件實(shí)例中的 _reactInternals 屬性,這個(gè)就是當(dāng)前組件所對(duì)應(yīng)的 Fiber 節(jié)點(diǎn)

function get(key) {
  return key._reactInternals;
}

題外話:react利用雙緩存機(jī)制來完成 Fiber 樹的構(gòu)建和替換,也就是 currentworkInProgress 兩棵樹,那 enqueueSetState 里面拿的是那棵樹下的 Fiber 節(jié)點(diǎn)呢?

答案是:current樹下的Fiber節(jié)點(diǎn)。具體的原理在下面update對(duì)象丟失問題再說明

創(chuàng)建update對(duì)象

function createUpdate(eventTime, lane) {
  var update = {
    eventTime: eventTime,
    lane: lane,
    tag: UpdateState,
    payload: null,
    callback: null,
    next: null
  };
  return update;
}

屬性的含義如下:

  • eventTime:update對(duì)象創(chuàng)建的時(shí)間,用于ensureRootIsScheduled計(jì)算過期時(shí)間用
  • lane:此次更新的優(yōu)先級(jí)
  • payload:setState的第一個(gè)參數(shù)
  • callback:setState的第二個(gè)參數(shù)
  • next:連接的下一個(gè) update 對(duì)象

將Update對(duì)象關(guān)聯(lián)到Fiber節(jié)點(diǎn)的updateQueue屬性

這里執(zhí)行的是 enqueueUpdate 函數(shù),下面是我簡(jiǎn)化過后的邏輯

function enqueueUpdate(fiber, update) {
    var updateQueue = fiber.updateQueue;
    var sharedQueue = updateQueue.shared;
    var pending = sharedQueue.pending;
    if (pending === null) {
      update.next = update;
    } else {
      update.next = pending.next;
      pending.next = update;
    }
    sharedQueue.pending = update;
}

可以看到這里的邏輯主要是將 update 對(duì)象放到 fiber 對(duì)象的 updateQueue.shared.pending 屬性中, updateQueue.shared.pending 是一個(gè)環(huán)狀鏈表。

那為什么需要把它設(shè)計(jì)為一個(gè)環(huán)狀鏈表?我是這樣理解的

  • shared.pending 存放的是鏈表的最后一個(gè)節(jié)點(diǎn),那么在環(huán)狀鏈表中,鏈表的最后一個(gè)節(jié)點(diǎn)的next指針,是指向環(huán)狀鏈表的頭部節(jié)點(diǎn),這樣我們就能快速知道鏈表的首尾節(jié)點(diǎn)
  • 當(dāng)知道首尾節(jié)點(diǎn)后,就能很輕松的合并兩個(gè)鏈表。比如有兩條鏈表a、b,我們想要把 b append到 a 的后面,可以這樣做
const lastBPoint = bTail
const firstBPoint = bTail.next
lastBPoint.next = null
aTail.next = firstBPoint
aTail = lastBPoint

后面即使有c、d鏈表,同樣也可以用相同的辦法合并到a。react 在構(gòu)建 updateQueue 鏈表上也用了類似的手法,新產(chǎn)生的 update 對(duì)象通過類似上面的操作合并到 updateQueue 鏈表,

發(fā)起調(diào)度

enqueueUpdate 末尾,執(zhí)行了 scheduleUpdateOnFiber 函數(shù),該方法最終會(huì)調(diào)用 ensureRootIsScheduled 函數(shù)來調(diào)度react的應(yīng)用根節(jié)點(diǎn)。

當(dāng)進(jìn)入 performConcurrentWorkOnRoot 函數(shù)時(shí),就代表進(jìn)入了 reconcile 階段,也就是我們說的 render 階段。render 階段是一個(gè)自頂向下再自底向上的過程,從react的應(yīng)用根節(jié)點(diǎn)開始一直向下遍歷,再從底部節(jié)點(diǎn)往上回歸,這就是render階段的節(jié)點(diǎn)遍歷過程。

這里我們需要知道的是,在render階段自頂向下遍歷的過程中,如果遇到組件類型的Fiber節(jié)點(diǎn),我們會(huì)執(zhí)行 processUpdateQueue 函數(shù),這個(gè)函數(shù)主要負(fù)責(zé)的是組件更新時(shí) state 的計(jì)算

processUpdateQueue做了什么

processUpdateQueue函數(shù)主要做了三件事情

  • 構(gòu)造本輪更新的 updateQueue,并緩存到 currentFiber 節(jié)點(diǎn)中
  • 循環(huán)遍歷 updateQueue,計(jì)算得到 newState,構(gòu)造下輪更新的 updateQueue
  • 更新 workInProgress 節(jié)點(diǎn)中的 updateQueue、memoizedState 屬性

這里的 updateQueue 并不指代源碼中 Fiber 節(jié)點(diǎn)的 updateQueue,可以理解為從 firstBaseUpdatelastBaseUpdate 的整條更新隊(duì)列。這里為了方便描述和理解,直接用 updateQueue 替代說明。

變量解釋

因?yàn)樯婕暗淖兞勘容^多,processUpdateQueue 函數(shù)的邏輯看起來并不怎么清晰,所以我先列出一些變量的解釋方便理解

  • shared.pending:

enqueueSetState 產(chǎn)生的 update對(duì)象 環(huán)形鏈表

  • first/lastBaseUpdate:-- 下面我會(huì)用 baseUpdate 代替

當(dāng)前 Fiber 節(jié)點(diǎn)中 updateQueue 對(duì)象中的屬性,代表當(dāng)前組件整個(gè)更新隊(duì)列鏈表的首尾節(jié)點(diǎn)

  • first/lastPendingUpdate:下面我會(huì)用 pendingUpdate 代替

shared.pending 剪開后的產(chǎn)物,分別代表新產(chǎn)生的 update對(duì)象 鏈表的首尾節(jié)點(diǎn),最終會(huì)合并到 currentFiber 和 workInProgress 兩棵樹的更新隊(duì)列尾部

  • newFirst/LastBaseUpdate:下面我會(huì)用 newBaseUpdate 代替

newState計(jì)算過程會(huì)得到,只要存在低優(yōu)先級(jí)的 update 對(duì)象,這兩個(gè)變量就會(huì)有值。這兩個(gè)變量會(huì)賦值給 workInProgress 的 baseUpdate,作為下一輪更新 update對(duì)象 鏈表的首尾節(jié)點(diǎn)

  • baseState:newState 計(jì)算過程依賴的初始 state
  • memoizedState:當(dāng)前組件實(shí)例的 state,processUpdateQueue 末尾會(huì)將 newState 賦值給這個(gè)變量,

構(gòu)造本輪更新的 updateQueue

上面我們說到 shared.pendingenqueueSetState 產(chǎn)生的 update對(duì)象 環(huán)形鏈表,在這里我們需要剪斷這個(gè)環(huán)形列表取得其中的首尾節(jié)點(diǎn),去組建我們的更新隊(duì)列。那如何剪斷呢?

shared.pending 是環(huán)形鏈表的尾部節(jié)點(diǎn),它的下一個(gè)節(jié)點(diǎn)就是環(huán)形鏈表的頭部節(jié)點(diǎn),參考上一小節(jié)我們提到的鏈表合并操作。

var lastPendingUpdate = shared.pending;
var firstPendingUpdate = lastPendingUpdate.next;
lastPendingUpdate.next = null;

這樣就能剪斷環(huán)形鏈表,拿到我們想要的新的 update 對(duì)象 —— pendingUpdate。接著我們要拿著這個(gè) pendingUpdate 做兩件事情:

  • pendingUpdate 合并到當(dāng)前Fiber節(jié)點(diǎn)的更新隊(duì)列
  • pendingUpdate 合并到 currentFiber樹 中對(duì)應(yīng) Fiber節(jié)點(diǎn) 的更新隊(duì)列

為什么要做這兩件事情?

  • 第一個(gè)是解決狀態(tài)連續(xù)性問題,當(dāng)出現(xiàn)多個(gè) setState 更新時(shí),我們要確保當(dāng)前 update對(duì)象 的更新是以前一個(gè) update對(duì)象 計(jì)算出來的 state 為前提。所以我們需要構(gòu)造一個(gè)更新隊(duì)列,新的 update對(duì)象 要合并到更新隊(duì)列的尾部,從而維護(hù)state計(jì)算的連續(xù)性
  • 第二個(gè)是解決 update 對(duì)象丟失問題。在 shared.pending 被剪開之后,shared.pending會(huì)被賦值為null,當(dāng)有高優(yōu)先級(jí)任務(wù)進(jìn)來時(shí),低優(yōu)先級(jí)任務(wù)就會(huì)被打斷,也就意味著 workInProgress 樹會(huì)被還原,shared.pending 剪開之后得到的 pendingUpdate 就會(huì)丟失。這時(shí)就需要將 pendingUpdate 合并到 currentFiber樹 的更新隊(duì)列中

接下來可以大致看一下這一部分的源碼

  var queue = workInProgress.updateQueue;
  var firstBaseUpdate = queue.firstBaseUpdate;
  var lastBaseUpdate = queue.lastBaseUpdate;
  // 1. 先拿到本次更新的 update對(duì)象 環(huán)形鏈表
  var pendingQueue = queue.shared.pending;
  if (pendingQueue !== null) {
    // 2. 清空pending
    queue.shared.pending = null;
    var lastPendingUpdate = pendingQueue;
    var firstPendingUpdate = lastPendingUpdate.next;
    // 3. 剪開環(huán)形鏈表
    lastPendingUpdate.next = null;
    // 4. 將 pendingupdate 合并到 baseUpdate
    if (lastBaseUpdate === null) {
      firstBaseUpdate = firstPendingUpdate;
    } else {
      lastBaseUpdate.next = firstPendingUpdate;
    }
    lastBaseUpdate = lastPendingUpdate;
    // 5. 將 pendingupdate 合并到 currentFiber樹的 baseUpdate
    var current = workInProgress.alternate;
    if (current !== null) {
      var currentQueue = current.updateQueue;
      var currentLastBaseUpdate = currentQueue.lastBaseUpdate;
      if (currentLastBaseUpdate !== lastBaseUpdate) {
        if (currentLastBaseUpdate === null) {
          currentQueue.firstBaseUpdate = firstPendingUpdate;
        } else {
          currentLastBaseUpdate.next = firstPendingUpdate;
        }
        currentQueue.lastBaseUpdate = lastPendingUpdate;
      }
    }
  }

源碼看起來很多,但本質(zhì)上只做了一件事,從源碼中可以看出這部分主要就是把 shared.pending 剪開,拿到我們的 pendingUpdate,再把 pendingUpdate 合并到本輪更新和 currentFiber 節(jié)點(diǎn)的 baseUpdate 中。

計(jì)算 newState

在這部分的源碼中,除了計(jì)算 newState,還有另外一個(gè)重要工作是,構(gòu)造下一輪更新用的 updateQueue。

到這里可能會(huì)有疑問,為什么需要構(gòu)造下輪更新的 updateQueue,本輪更新我們把 shared.pending 里面的對(duì)象遍歷計(jì)算完,再把 state 更新,下輪更新進(jìn)來再根據(jù)這個(gè) state 計(jì)算不行好了嗎?

如果沒有高優(yōu)先級(jí)任務(wù)打斷機(jī)制,確實(shí)是不需要在這里構(gòu)造下輪更新的 updateQueue,因?yàn)槊枯喐挛覀冎粫?huì)依賴當(dāng)前的 state 和 shared.pending。

打斷機(jī)制下,低優(yōu)先級(jí)任務(wù)重啟后的執(zhí)行,需要依賴完整的更新隊(duì)列才能保證 state 的連續(xù)性和正確性。下面我舉個(gè)例子

state = { count: 0 }
componentDidMount() {
    const button = this.buttonRef.current
    // 低優(yōu)先級(jí)任務(wù)
    setTimeout(() => this.setState({ count: 1 }), 1000)
    // 高優(yōu)先級(jí)任務(wù)
    setTimeout(() => button.click(), 1040)
}
handleButtonClick = () => {
    this.setState( prevState => {
      return { count: prevState.count + 2 }
    } )
}

我們期望能實(shí)現(xiàn)的效果是 0 -> 2 -> 3,需求如下:

  • 高優(yōu)先級(jí)任務(wù)打斷低優(yōu)先級(jí)任務(wù)之后,不以低優(yōu)先級(jí)任務(wù)計(jì)算得到的baseState做計(jì)算
  • 低優(yōu)先級(jí)任務(wù)重啟后,不能覆蓋高優(yōu)先級(jí)任務(wù)計(jì)算得到的值,且需要根據(jù)低優(yōu)先級(jí)任務(wù)計(jì)算得到的newState,作為高優(yōu)先級(jí)的baseState再去執(zhí)行一次高優(yōu)先級(jí)任務(wù)

知道了需求,我們可以大概列一下實(shí)現(xiàn)思路:

  • 低優(yōu)先級(jí)任務(wù)打斷后,高優(yōu)先級(jí)任務(wù)執(zhí)行之前,需要還原到低優(yōu)先級(jí)任務(wù)執(zhí)行之前的 workInPregress 節(jié)點(diǎn),確保不受低優(yōu)先級(jí)任務(wù)計(jì)算得到的 baseState 影響
  • 需要維護(hù)一個(gè)更新對(duì)象隊(duì)列,按執(zhí)行順序存儲(chǔ) update 對(duì)象,確保低優(yōu)先級(jí)重啟后,依然會(huì)執(zhí)行高優(yōu)先級(jí)任務(wù)

上面說的需求和實(shí)現(xiàn)思路在 react 的源碼中實(shí)現(xiàn)其實(shí)是非常簡(jiǎn)單的,但要理解其中的含義可能需要費(fèi)點(diǎn)功夫,下面可以看看我改動(dòng)過后的源碼,可以直接從 do...while 開始看

  function cloneUpdate(update) {
      return {
          eventTime: update.eventTime,
          lane: update.lane,
          tag: update.tag,
          payload: update.payload,
          callback: update.callback,
          next: null
      };
  }
  if (firstBaseUpdate !== null) {
    var newState = queue.baseState;
    var newBaseState = null;
    var newFirstBaseUpdate = null;
    var newLastBaseUpdate = null;
    var update = firstBaseUpdate;
    // 遍歷 updateQueue
    do {
      var updateLane = update.lane;
      var updateEventTime = update.eventTime;
      // 校驗(yàn)當(dāng)前 update 對(duì)象夠不夠優(yōu)先級(jí)
      if (!isSubsetOfLanes(renderLanes, updateLane)) {
        // 優(yōu)先級(jí)不夠,我們需要從當(dāng)前 update 對(duì)象開始重新構(gòu)造一個(gè)更新隊(duì)列
        var clone = cloneUpdate(update)
        if (newLastBaseUpdate === null) {
          newFirstBaseUpdate = newLastBaseUpdate = clone;
          // 當(dāng)前的 newState 就作為下輪更新的 baseState 使用
          newBaseState = newState;
        } else {
          newLastBaseUpdate = newLastBaseUpdate.next = clone;
        }
      } else {
        // 優(yōu)先級(jí)夠
        if (newLastBaseUpdate !== null) {
          // newLastBaseUpdate 不為空,就代表存在優(yōu)先級(jí)不夠的 update 對(duì)象
          var _clone = cloneUpdate(update)
          // 為保證狀態(tài)連續(xù)性,即使當(dāng)前 update 對(duì)象優(yōu)先級(jí)足夠,也要被放到 updateQueue 中
          newLastBaseUpdate = newLastBaseUpdate.next = _clone;
        }
        // 計(jì)算newState
        newState = getStateFromUpdate(workInProgress, queue, update, newState, props, instance);
      }
      update = update.next;
    } while (update);

邏輯如下:

優(yōu)先級(jí)不夠

  • 重新構(gòu)造更新隊(duì)列 newBaseUpdate,留到低優(yōu)先級(jí)任務(wù)重啟遍歷
  • 記錄當(dāng)前 newState,留到低優(yōu)先級(jí)任務(wù)重啟作為 baseState 計(jì)算

優(yōu)先級(jí)足夠

  • 看看 newBaseUpdate 有沒有東西,有東西就把當(dāng)前 update 對(duì)象也合并進(jìn)去
  • 計(jì)算 newState

這里 newState 的計(jì)算邏輯很簡(jiǎn)單

  • payload是值。用對(duì)象包裹合并到 prevState 即可
  • payload是函數(shù)。傳入 prevState 計(jì)算,將函數(shù)返回值也合并到 prevState 即可

更新 workInProgress 節(jié)點(diǎn)

更新 workInProgress 節(jié)點(diǎn)屬性的邏輯不多,主要就是把 newBaseState、newBaseUpate 賦值給 workInProgress 節(jié)點(diǎn),作為下一輪更新的 baseState 和更新隊(duì)列使用

if (newLastBaseUpdate === null) {
  newBaseState = newState;
}
queue.baseState = newBaseState;
queue.firstBaseUpdate = newFirstBaseUpdate;
queue.lastBaseUpdate = newLastBaseUpdate;
workInProgress.memoizedState = newState;
  • 如果 newLastBaseUpdate 為空,代表所有 update 對(duì)象為空,本輪更新計(jì)算得到的 newState 可以完全作為下輪更新的 baseState 使用。否則只能用出現(xiàn)首個(gè)不夠優(yōu)先級(jí)的 update 對(duì)象時(shí)緩存下來的 newState 作為下輪更新的 baseState
  • 更新 baseUpdate,當(dāng)所有 update 對(duì)象優(yōu)先級(jí)足夠,baseUpdate 的值一般為空。只有存在優(yōu)先級(jí)不夠的 update 對(duì)象時(shí),才會(huì)有值
  • newState 賦值給 memoizedState, memoizedState 代表當(dāng)前組件的所有 state

總結(jié)

看到上面的原理解析是不是很復(fù)雜,我們可以忽略所有的實(shí)現(xiàn)細(xì)節(jié),回歸現(xiàn)象本質(zhì),state計(jì)算就是遍歷 update對(duì)象 鏈表根據(jù) payload 得到新的state。在此前提下,因?yàn)閮?yōu)先級(jí)機(jī)制,打斷之后會(huì)還原 workInProgress 節(jié)點(diǎn),從而會(huì)引起 update對(duì)象 丟失問題 和 state計(jì)算連續(xù)性問題。解決這兩個(gè)問題才是我們上面說的復(fù)雜的實(shí)現(xiàn)細(xì)節(jié)

update對(duì)象丟失問題

為什么會(huì)丟失

我們知道高優(yōu)先級(jí)任務(wù)進(jìn)來會(huì)打斷低優(yōu)先級(jí)任務(wù)的執(zhí)行,打斷之后會(huì)將當(dāng)前的 workInProgress 節(jié)點(diǎn)還原為開始的狀態(tài),也就是可以理解為會(huì)將 workInProgress 樹還原為當(dāng)前頁面所渲染的 currentFiber 節(jié)點(diǎn)。當(dāng) workInProgress 節(jié)點(diǎn)還原之后,我們本來存在 workInProgress 中的 updateQueue 屬性也會(huì)被重置,那就意味著低優(yōu)先級(jí)的 update 對(duì)象會(huì)丟失。

上面說到的,setState產(chǎn)生的新 update對(duì)象 是會(huì)放在 currentFiber 節(jié)點(diǎn)上也是這個(gè)原因,如果 setState 產(chǎn)生的新 update對(duì)象 放到 workInProgress 上,只要 workInProgress 被還原,這些 update對(duì)象 就會(huì)丟失

如何解決

我們?cè)?processUpdateQueue 函數(shù)的開始階段,將新產(chǎn)生的 update 對(duì)象,也就是 shared.pending 中的值,合并到 currentFiber( workInProgress.alternate ) 節(jié)點(diǎn)的 firstBaseUpdatelastBaseUpdate。具體規(guī)則如下

  • currentFiber 節(jié)點(diǎn)不存在 lastBaseUpdate,將新的 update 對(duì)象賦值給 currentFiber 節(jié)點(diǎn)的 firstBaseUpdatelastBaseUpdate 屬性
  • currentFiber 節(jié)點(diǎn)存在 lastBaseUpdate,將新的 update 對(duì)象拼接到 currentFiber 節(jié)點(diǎn)的 lastBaseUpdate 節(jié)點(diǎn)后面,也就是說新的 update 對(duì)象會(huì)成為 currentFiber 節(jié)點(diǎn)新的 lastBaseUpdat 節(jié)點(diǎn)

還原 workInProgress 節(jié)點(diǎn)執(zhí)行的函數(shù)是 prepareFreshStack,里面會(huì)用 currentFiber 節(jié)點(diǎn)的屬性覆蓋 workInProgress 節(jié)點(diǎn),從而實(shí)現(xiàn)還原功能。所以就算 workInProgress 節(jié)點(diǎn)被重置,我們只要把 update對(duì)象 合并到 currentFiber 節(jié)點(diǎn)上,還原的時(shí)候依然會(huì)存在于新的 workInProgress 節(jié)點(diǎn)

state計(jì)算的連續(xù)性

問題現(xiàn)象

我們上面說到,低優(yōu)先級(jí)任務(wù)重啟,不能覆蓋高優(yōu)先級(jí)任務(wù)計(jì)算得到的值,且需要根據(jù)低優(yōu)先級(jí)任務(wù)計(jì)算得到的newState,作為高優(yōu)先級(jí)的baseState再去執(zhí)行一次高優(yōu)先級(jí)任務(wù)。什么意思呢這是?

state = { count: 0 }
componentDidMount() {
    const button = this.buttonRef.current
    // 低優(yōu)先級(jí)任務(wù) - AUpate
    setTimeout(() => this.setState({ count: 1 }), 1000)
    // 高優(yōu)先級(jí)任務(wù) - BUpdate
    setTimeout(() => button.click(), 1040)
}
handleButtonClick = () => {
    this.setState( prevState => {
      return { count: prevState.count + 2 }
    } )
}

上面代碼所產(chǎn)生的update對(duì)象如下

AUpate = { lane: 低, payload: 1 }
BUpdate = { lane: 高, payload: state => ({ count: state.count + 2 }) }
  • 先執(zhí)行 AUpdate 任務(wù)
  • AUpdate 的優(yōu)先級(jí)比 BUpdate 的低,BUpdate 會(huì)打斷 AUpdate 的執(zhí)行。
  • 那么 BUpdate 執(zhí)行完,count的值為2 問題來了
  • BUpdate 是后進(jìn)來的,AUpdate 不能覆蓋掉 BUpdate 的結(jié)果
  • AUpdate 執(zhí)行的結(jié)果 count 會(huì)變成 1,那么 BUpdate 的結(jié)果需要在此基礎(chǔ)上計(jì)算,也就是要得到3

這也就決定了我們要用隊(duì)列的形式去存儲(chǔ)所有 update對(duì)象。update對(duì)象的存儲(chǔ)順序決定了state計(jì)算的前后依賴性,從而保證狀態(tài)的連續(xù)性和準(zhǔn)確性

明確很重要的一點(diǎn),優(yōu)先級(jí)高低只會(huì)影響某個(gè) update對(duì)象 是否會(huì)提前執(zhí)行,不會(huì)影響最終的 state 結(jié)果。最終的 state 結(jié)果還是由更新隊(duì)列中 update對(duì)象 的順序決定的

如何解決

我們看到 processUpdateQueue 中有兩部分都是在構(gòu)造更新隊(duì)列的

  • 一部分是位于函數(shù)開頭的,將 update對(duì)象 合并到 currentFiber 節(jié)點(diǎn)
  • 一部分是位于函數(shù)末尾的,將 newBaseUpdate 賦值給 workInProgress 節(jié)點(diǎn) 這兩部分雙劍合璧就完美解決我們的需求,currentFiber 是作用于本輪更新,workInProgress 則作用于下一輪更新,因?yàn)殡p緩存機(jī)制的存在,在 commit階段 結(jié)尾,react 應(yīng)用根節(jié)點(diǎn)的 current 指針就會(huì)指向 workInProgress 節(jié)點(diǎn),workInProgress 節(jié)點(diǎn)在下一輪更新就會(huì)變成 currentFiber 節(jié)點(diǎn)。

這樣無論是什么優(yōu)先級(jí),只要按順序構(gòu)造出更新隊(duì)列,我就能計(jì)算出正確的newState,同時(shí)利用隊(duì)列的性質(zhì),保證 update對(duì)象 間 state計(jì)算 的連續(xù)性

以上就是React源碼state計(jì)算流程和優(yōu)先級(jí)實(shí)例解析的詳細(xì)內(nèi)容,更多關(guān)于React state計(jì)算流程優(yōu)先級(jí)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • React-redux?中useSelector使用源碼分析

    React-redux?中useSelector使用源碼分析

    在一個(gè) action 被分發(fā)(dispatch) 后,useSelector() 默認(rèn)對(duì) select 函數(shù)的返回值進(jìn)行引用比較 ===,并且僅在返回值改變時(shí)觸發(fā)重渲染,,這篇文章主要介紹了React-redux?中useSelector使用,需要的朋友可以參考下
    2023-10-10
  • React高級(jí)概念之Context用法詳解

    React高級(jí)概念之Context用法詳解

    在React應(yīng)用中,為了讓數(shù)據(jù)在組件間共享,常見的方式是讓它們以props的形式自頂向下傳遞,如果數(shù)據(jù)要在組件樹不同層級(jí)共享,那么這些數(shù)據(jù)必須逐層傳遞直到目的地,Context如同管道,它將數(shù)據(jù)從入口直接傳遞到出口,使用Context能避免“prop-drilling”
    2023-06-06
  • react native 獲取地理位置的方法示例

    react native 獲取地理位置的方法示例

    這篇文章主要介紹了react native 獲取地理位置的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-08-08
  • React中父組件如何獲取子組件的值或方法

    React中父組件如何獲取子組件的值或方法

    這篇文章主要介紹了React中父組件如何獲取子組件的值或方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • 詳解React Native 采用Fetch方式發(fā)送跨域POST請(qǐng)求

    詳解React Native 采用Fetch方式發(fā)送跨域POST請(qǐng)求

    這篇文章主要介紹了詳解React Native 采用Fetch方式發(fā)送跨域POST請(qǐng)求,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-11-11
  • JavaScript中的useRef 和 useState介紹

    JavaScript中的useRef 和 useState介紹

    這篇文章主要給大家分享的是 JavaScript中的useRef 和 useState介紹,下列文章,我們將學(xué)習(xí) useRef 和 useState hook是什么,它們的區(qū)別以及何時(shí)使用哪個(gè)。 這篇文章中的代碼示例將僅涉及功能組件,但是大多數(shù)差異和用途涵蓋了類和功能組件,需要的朋友可以參考一下
    2021-11-11
  • React Native中TabBarIOS的簡(jiǎn)單使用方法示例

    React Native中TabBarIOS的簡(jiǎn)單使用方法示例

    最近在學(xué)習(xí)過程中遇到了很多問題,TabBarIOS的使用就是一個(gè),所以下面這篇文章主要給大家介紹了關(guān)于React Native中TabBarIOS簡(jiǎn)單使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。
    2017-10-10
  • 使用react render props實(shí)現(xiàn)倒計(jì)時(shí)的示例代碼

    使用react render props實(shí)現(xiàn)倒計(jì)時(shí)的示例代碼

    這篇文章主要介紹了使用react render props實(shí)現(xiàn)倒計(jì)時(shí)的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-12-12
  • React Agent 自定義實(shí)現(xiàn)代碼

    React Agent 自定義實(shí)現(xiàn)代碼

    在使用langchain的ReactAgent遇到問題后,作者嘗試自定義ReactAgent實(shí)現(xiàn),通過詳細(xì)分析langchain中的agent功能和問題,結(jié)合React思想,作者設(shè)計(jì)了新的agent邏輯并在GitHub上分享了代碼,新的ReactAgent通過改進(jìn)prompt和工具調(diào)用邏輯,提升了任務(wù)執(zhí)行的效果和穩(wěn)定性
    2024-10-10
  • react項(xiàng)目實(shí)踐之webpack-dev-serve

    react項(xiàng)目實(shí)踐之webpack-dev-serve

    這篇文章主要介紹了react項(xiàng)目實(shí)踐之webpack-dev-serve,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-09-09

最新評(píng)論

调兵山市| 泰安市| 宜兰市| 德格县| 科尔| 定远县| 霍州市| 彩票| 咸阳市| 阿瓦提县| 桑日县| 昂仁县| 阳东县| 嫩江县| 根河市| 双城市| 伊金霍洛旗| 牟定县| 尼木县| 小金县| 盖州市| 兴和县| 信宜市| 修水县| 宁安市| 靖西县| 周宁县| 新兴县| 呼伦贝尔市| 商南县| 顺义区| 龙海市| 蒲江县| 萨嘎县| 高平市| 加查县| 乌鲁木齐市| 施秉县| 蚌埠市| 信阳市| 囊谦县|