欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

useEffect?返回函數(shù)執(zhí)行過程源碼解析

 更新時間:2023年04月17日 09:59:10   作者:uccs  
這篇文章主要為大家介紹了useEffect?返回函數(shù)執(zhí)行過程源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

本文對應(yīng)的 react 版本是 18.2.0

掌握 React 組件樹遍歷技巧中說到 react 是怎么遍歷 dom

那么在遍歷的過程中,如果發(fā)現(xiàn)當(dāng)前節(jié)點有子節(jié)點被刪除了,那么 react 會怎么處理呢?

下面是源碼簡化:這里是完整的源碼

function recursivelyTraversePassiveUnmountEffects(parentFiber: Fiber): void {
  const deletions = parentFiber.deletions;
  if ((parentFiber.flags & ChildDeletion) !== NoFlags) {
    if (deletions !== null) {
      for (let i = 0; i < deletions.length; i++) {
        const childToDelete = deletions[i];
        nextEffect = childToDelete;
        commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
          childToDelete,
          parentFiber
        );
      }
    }
  }
}
function commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
  deletedSubtreeRoot: Fiber,
  nearestMountedAncestor: Fiber | null
) {
  while (nextEffect !== null) {
    const fiber = nextEffect;
    // 執(zhí)行 passive effects 返回的函數(shù)
    commitPassiveUnmountInsideDeletedTreeOnFiber(fiber, nearestMountedAncestor);
    const child = fiber.child;
    if (child !== null) {
      child.return = fiber;
      nextEffect = child;
    } else {
      commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
        deletedSubtreeRoot
      );
    }
  }
}
function commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
  deletedSubtreeRoot
) {
  while (nextEffect !== null) {
    const fiber = nextEffect;
    const sibling = fiber.sibling;
    const returnFiber = fiber.return;
    if (fiber === deletedSubtreeRoot) {
      nextEffect = null;
      return;
    }
    if (sibling !== null) {
      sibling.return = returnFiber;
      nextEffect = sibling;
      return;
    }
    nextEffect = returnFiber;
  }
}

deletions

在正式開始之前,我們要了解一個 fiber 的屬性:deletions

這個屬性存放的是當(dāng)前節(jié)點中被刪除的 fiber,這個數(shù)組是在 commit 階段被賦值的

如果有被刪除的節(jié)點,這個屬性值是一個數(shù)組,如果沒有被刪除的節(jié)點,這個屬性值是 null

const A = () => {
  useEffect(() => {
    return () => {
      console.log("A unmount");
    };
  }, []);
  return <div>文本A</div>;
};
const B = () => {
  useEffect(() => {
    return () => {
      console.log("B unmount");
    };
  }, []);
  return <div>文本B</div>;
};

如果 App 組件這樣寫,那么 deletions 的值是 [FiberNode, FiberNode]

const App(){
  const [count, setCount] = useState(0)
  return <div>
    {count % 2 === 0 && <A />}
    {count % 2 === 0 && <B />}
    <div onClick={()=> setCount(count+1)}>+1</div>
  </div>
}

如果 App 組件這樣寫,那么 deletions 的值是 [FiberNode]

const App(){
  const [count, setCount] = useState(0)
  return <div>
    {count % 2 === 0 && <><A /><B /></>}
    <div onClick={()=> setCount(count+1)}>+1</div>
  </div>
}

對于第二種情況,react 會把 A 組件和 B 組件作為一個整體,所以 deletions 的值是 [FiberNode]

處理當(dāng)前節(jié)點的 deletions

react 在遍歷 fiber tree 時,會先處理當(dāng)前的 fiberdeletions,等處理完之后再遍歷下一個 fiber

現(xiàn)在我們已經(jīng)知道 deletions 中保存的是當(dāng)前 fiber 下被刪除的子節(jié)點

這時 react 會遍歷 deletions 數(shù)組,然后執(zhí)行每個 fiberpassive effect 返回的函數(shù)

但是有個問題,如果 deletions 中的 fiber 有子節(jié)點,那么這些子節(jié)點也會被刪除,這時 react 會怎么處理呢?

這里分兩種情況來討論:

  • 刪除的 fiber 沒有子節(jié)點:<div>{xxxx && <A />}</div>
  • 刪除的 fiber 有子節(jié)點:<div>{xxxx && <><A /><B /></>}</div> -->

刪除的 fiber 沒有子節(jié)點:

<div>{xxxx && <A />}</div>

這種情況比較好理解

當(dāng)遍歷到 div 時,因為 <A/> 節(jié)點會被卸載,所以在 divdeletions 保存了一個 <A/>fiber

遍歷 deletions 數(shù)組,執(zhí)行 <A/>passive effect 返回的函數(shù)

如下圖所示:

刪除的 fiber 有子節(jié)點:

<div>{xxxx && <><A /><B /></>}</div>

這種情況就比較復(fù)雜了

當(dāng)遍歷到 div 時,<></> 節(jié)點會被卸載,所以在 divdeletions 保存了一個 <></>fiber

遍歷 deletions 數(shù)組,執(zhí)行 fiberpassive effect 返回的函數(shù),對于 <></> 來說是不存在的 passive effect

那么這個時候就要去遍歷它的 child.fiber,也就是 <A/><B/>

首先拿到第一個 fiber,也就是 <A/>,然后執(zhí)行 <A/>passive effect 返回的函數(shù),這步比較好理解

child = fiber.child;
if (child !== null) {
  nextEffect = child;
}

這里遍歷也是深度優(yōu)先,遍歷一個 child,執(zhí)行一個 passive effect 返回函數(shù),然后再遍歷下一個 child(這邊 <A /> 已經(jīng)是葉子節(jié)點了)

然后拿到第二個 fiber,也就是 <B/>,然后執(zhí)行 <B/>passive effect 返回的函數(shù),這步就不太好理解了

child = fiber.child;
if (child !== null) {
  nextEffect = child;
} else {
  commitPassiveUnmountEffectsInsideOfDeletedTree_complete(deletedSubtreeRoot);
}

這里要注意的是:

react 在尋找有 passive effectfiber 時,只遍歷到有 passive effectfiber, 像 div 這種沒有 passive effect 就不會遍歷

但是在處理 deletions,react 會遍歷所有的 fiber,也就是說從當(dāng)前的 fiber 開始,一直往下遍歷到葉子節(jié)點,這個葉子節(jié)點是指文本節(jié)點這種,往下不會有節(jié)點了(對于 A 組件來說 文本A 是文本節(jié)點)

然后在開始往上遍歷,往上遍歷是調(diào)用 commitPassiveUnmountEffectsInsideOfDeletedTree_complete 函數(shù),直到遍歷到 deletionRoot,在向上遍歷的過程中會檢查是否有 sibling,如果有說明 sibling 還沒被處理,這樣就找到了 <B/>,然后執(zhí)行 <B/>passive effect 返回的函數(shù)

如下圖所示:

向下遍歷和向上遍歷

在處理 deletions 時,對于每個 deletedNode,都先向下遍歷,然后再向上遍歷

  • 向下遍歷:commitPassiveUnmountEffectsInsideOfDeletedTree_begin(深度優(yōu)先,優(yōu)先處理左邊的節(jié)點)
  • 向上遍歷:commitPassiveUnmountEffectsInsideOfDeletedTree_complete(之后再處理右邊節(jié)點)

總結(jié)

1. 遍歷 deletions 數(shù)組:

  • react 在處理 deletions 時,先沿著 fiber tree 向下遍歷,如果有 passive effect 返回的函數(shù),則執(zhí)行
  • 一直遍歷到?jīng)]有 childfiber,再向上遍歷,處理 sibling
  • 再向上遍歷時,如果如果遇到 sibling,再向下遍歷,向下遍歷時遇到 passive effect 返回的函數(shù),則執(zhí)行
  • 如此循環(huán)直到遍歷到 deletedNode,結(jié)束遍歷

2. 結(jié)合掌握 React 組件樹遍歷技巧

  • 遍歷尋找有 passive effect 節(jié)點
    • react 從根組件向下遍歷,如果沒有 passive effect,則不會遍歷
  • 遍歷時,如果遇到當(dāng)前節(jié)點有 deletions 時,會暫停尋找 passive effect 節(jié)點
    • 進入遍歷 deletions 數(shù)組

react 遍歷 deletions 完整邏輯如下圖所示:

圖中綠色部分是遍歷 deletionsNode 過程,紅色部分是遍歷尋找 passive effect 過程

以上就是useEffect 返回函數(shù)執(zhí)行過程源碼解析的詳細(xì)內(nèi)容,更多關(guān)于useEffect 返回函數(shù)執(zhí)行的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 一起來學(xué)習(xí)React元素的創(chuàng)建和渲染

    一起來學(xué)習(xí)React元素的創(chuàng)建和渲染

    這篇文章主要為大家詳細(xì)介紹了React元素的創(chuàng)建和渲染,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • 解決React報錯React.Children.only expected to receive single React element child

    解決React報錯React.Children.only expected to rece

    這篇文章主要為大家介紹了React報錯React.Children.only expected to receive single React element child分析解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • React如何接收excel文件下載導(dǎo)出功能封裝

    React如何接收excel文件下載導(dǎo)出功能封裝

    這篇文章主要介紹了React如何接收excel文件下載導(dǎo)出功能封裝,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • React?createRef循環(huán)動態(tài)賦值ref問題

    React?createRef循環(huán)動態(tài)賦值ref問題

    這篇文章主要介紹了React?createRef循環(huán)動態(tài)賦值ref問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • react組件基本用法示例小結(jié)

    react組件基本用法示例小結(jié)

    這篇文章主要介紹了react組件基本用法,結(jié)合實例形式分析了react組件傳值、生命周期、受控組件和非受控組件等相關(guān)操作技巧,需要的朋友可以參考下
    2020-04-04
  • React html中使用react的兩種方式

    React html中使用react的兩種方式

    這篇文章主要介紹了React html中使用react的兩種方式,本文給大家提到了React pwa的配置代碼,給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-04-04
  • React中使用react-file-viewer問題

    React中使用react-file-viewer問題

    這篇文章主要介紹了React中使用react-file-viewer問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • 基于antd的autocomplete的二次封裝查詢示例

    基于antd的autocomplete的二次封裝查詢示例

    這篇文章主要為大家介紹了基于antd的autocomplete的二次封裝查詢示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • react入門級詳細(xì)筆記

    react入門級詳細(xì)筆記

    這篇文章講述了React的基本介紹,基本使用和React相關(guān)js庫.通過這篇文章可以入門React的使用,可以快速上手使用React進行代碼的編寫
    2021-06-06
  • 淺談React Native 傳參的幾種方式(小結(jié))

    淺談React Native 傳參的幾種方式(小結(jié))

    這篇文章主要介紹了淺談React Native 傳參的幾種方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05

最新評論