日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無先后,達(dá)者為師

網(wǎng)站首頁 編程語言 正文

React?中state與props更新深入解析_React

作者:何遇er ? 更新時(shí)間: 2022-12-12 編程語言

正文

在這篇文章中,我使用下面這樣的應(yīng)用程序作為例子

class ClickCounter extends React.Component {
    constructor(props) {
        super(props);
        this.state = {count: 0};
        this.handleClick = this.handleClick.bind(this);
    }
    handleClick() {
        this.setState((state) => {
            return {count: state.count + 1};
        });
    }
    componentDidUpdate() {
        // todo 
    }
    render() {
        return [
            <button key="1" onClick={this.handleClick}>Update counter</button>,
            <span key="2">{this.state.count}</span>
        ]
    }
}

我給 ClickCounter 組件添加了 componentDidUpdate 鉤子,這個(gè)鉤子會(huì)在 commit 階段被調(diào)用。

在之前我寫了一篇深入介紹 React Fiber的文章,在那篇文章中我介紹了 React 團(tuán)隊(duì)為什么要重新實(shí)現(xiàn) reconciliation 算法、fiber 節(jié)點(diǎn)與 react element 的關(guān)系、fiber 節(jié)點(diǎn)的字段以及 fiber 節(jié)點(diǎn)是如何被組織在一起的。在這篇文章中我將介紹 React 如何處理 state 更新以及 React 如何創(chuàng)建 effects list,我也會(huì)介紹在 render 階段和 commit 階段調(diào)用的函數(shù)。

組件的 updater

當(dāng)我們點(diǎn)擊按鈕之后 handleClick 方法會(huì)被調(diào)用,這導(dǎo)致 state.count 值加 1

class ClickCounter extends React.Component {
    ...
    handleClick() {
        this.setState((state) => {
            return {count: state.count + 1};
        });
    }
}   

每個(gè) React 組件都有一個(gè)相關(guān)聯(lián)的 updater,它作為組件與 React core 之間的橋梁,這允許 ReactDOM、React Native、服務(wù)器端渲染和測試工具以不同的方式實(shí)現(xiàn) setState。這篇文章我們只討論在 ReactDOM 中 updater 的實(shí)現(xiàn),ClickCounter 組件的 updater 是一個(gè) classComponentUpdater,它負(fù)責(zé)檢索 fiber 實(shí)例,隊(duì)列更新和工作調(diào)度。

const classComponentUpdater = {
  isMounted,
  enqueueSetState(inst, payload, callback) {
    const fiber = ReactInstanceMap.get(inst);
    const currentTime = requestCurrentTime();
    const expirationTime = computeExpirationForFiber(currentTime, fiber);
    ...
  },
  enqueueReplaceState(inst, payload, callback) {
    const fiber = ReactInstanceMap.get(inst);
    const currentTime = requestCurrentTime();
    const expirationTime = computeExpirationForFiber(currentTime, fiber);
    ...
    enqueueUpdate(fiber, update);
    scheduleWork(fiber, expirationTime);
  },
  enqueueForceUpdate(inst, callback) {
    const fiber = ReactInstanceMap.get(inst);
    const currentTime = requestCurrentTime();
    const expirationTime = computeExpirationForFiber(currentTime, fiber);
    ...
    enqueueUpdate(fiber, update);
    scheduleWork(fiber, expirationTime);
  },
};

當(dāng) update 發(fā)生時(shí),它們被添加到 fiber 節(jié)點(diǎn)上的 updateQueue 中處理。在我們的例子中,ClickCounter 組件的 fiber 節(jié)點(diǎn)的結(jié)構(gòu)如下:

{
    stateNode: new ClickCounter,
    type: ClickCounter,
    updateQueue: {
         baseState: {count: 0}
         firstUpdate: {
             next: {
                 payload: (state) => { return {count: state.count + 1} }
             }
         },
         ...
     },
     ...
}

如果你仔細(xì)觀察,你會(huì)發(fā)現(xiàn) updateQueue.firstUpdate.next.payload 的值是我們在 ClickCounter 組件中傳遞給 setState 的參數(shù)。它代表在 render 階段需要處理的第一個(gè) update。

處理 ClickCounter Fiber 的 update

我在上一篇文章中介紹了 nextUnitOfWork 變量的作用,nextUnitOfWork 保存了對workInProgress樹中 fiber 節(jié)點(diǎn)的引用,當(dāng) React 遍歷 fiber 樹時(shí),它使用這個(gè)變量來判斷是否有其他未完成工作的 fiber 節(jié)點(diǎn)。

在調(diào)用 setState 方法之后,React 把我們傳遞給 setState 的參數(shù)添加到 ClickCounter fiber 的 updateQueue 屬性上并且進(jìn)行工作調(diào)度。React 進(jìn)入 render 階段,它使用renderRoot函數(shù)從 fiber 樹最頂層的 HostRoot 開始遍歷 fiber,在遍歷的過程中 React 會(huì)跳過已經(jīng)處理完的 fiber 直到遇到?jīng)]有處理的 fiber。在 render 階段,fiber 節(jié)點(diǎn)的所有工作都是在 fiber 的 alternate 字段上進(jìn)行的。如果還沒有創(chuàng)建 alternate,React 會(huì)在處理 update 之前在createWorkInProgress 函數(shù)中創(chuàng)建 alternate。

在這里我們假設(shè) nextUnitOfWork 變量中保存的是 ClickCounter fiber 的 alternate。

beginWork

在處理 update 時(shí),首先會(huì)調(diào)用 beginWork 函數(shù)。

由于 fiber 樹上的每一個(gè) fiber 都會(huì)執(zhí)行 beginWork 函數(shù),如果你想 debug render 階段,你可以在 beginWork 函數(shù)中打斷點(diǎn)。

beginWork 函數(shù)基本上包含了一個(gè)大的 switch 語句,switch 通過判斷 fiber 的 tag 來確定 fiber 需要做哪些工作

function beginWork(current, workInProgress, ...) {
    ...
    switch (workInProgress.tag) {
        ...
        case FunctionalComponent: {...}
        case ClassComponent:
        {
            ...
            return updateClassComponent(current$$1, workInProgress, ...);
        }
        case HostComponent: {...}
        case ...
}

由于 ClickCounter 是一個(gè)類組件,所以 React 會(huì)執(zhí)行updateClassComponent函數(shù),updateClassComponent 函數(shù)大概如下:

function updateClassComponent(current, workInProgress, Component, ...) {
    ...
    const instance = workInProgress.stateNode;
    let shouldUpdate;
    if (instance === null) {
        ...
        // In the initial pass we might need to construct the instance.
        constructClassInstance(workInProgress, Component, ...);
        mountClassInstance(workInProgress, Component, ...);
        shouldUpdate = true;
    } else if (current === null) {
        // In a resume, we'll already have an instance we can reuse.
        shouldUpdate = resumeMountClassInstance(workInProgress, Component, ...);
    } else {
        shouldUpdate = updateClassInstance(current, workInProgress, ...);
    }
    return finishClassComponent(current, workInProgress, Component, shouldUpdate, ...);
}

在 updateClassComponent 函數(shù)中 React 會(huì)判斷組件是否是第一次 render、是否是恢復(fù)工作或者是否是 update,不同的情況做的事情不一樣。

在上面的例子中,當(dāng)我們點(diǎn)擊按鈕調(diào)用 setState 方法時(shí),我們已經(jīng)有 ClickCounter 組件實(shí)例了,所以 React 會(huì)調(diào)用updateClassInstance方法,在 updateClassInstance 函數(shù)中會(huì)按下面的順序執(zhí)行很多函數(shù):

  • 調(diào)用 UNSAFE_componentWillReceiveProps 鉤子(deprecated)
  • 處理 updateQueue 中的 update 并生成新的 state
  • 使用這個(gè)新 state 調(diào)用 getDerivedStateFromProps 并獲得組件最終的 state
  • 調(diào)用 shouldComponentUpdate 鉤子去確定組件是否需要更新;如果不需要更新就跳過整個(gè) render 階段(不調(diào)用組件和組件 children 的 render 方法);否則繼續(xù)更新
  • 調(diào)用 UNSAFE_componentWillUpdate 鉤子(deprecated)
  • 添加觸發(fā) componentDidUpdate 鉤子的 effect
  • 更新組件實(shí)例的 state 和 props

雖然 componentDidUpdate 鉤子的 effect 是在 render 階段被添加的,但是 componentDidUpdate 鉤子會(huì)在 commit 階段執(zhí)行

組件的 state 和 props 會(huì)在調(diào)用 render 方法之前被更新,因?yàn)?render 方法的輸出依賴于 state 和 props 的值。

下面是 updateClassInstance 函數(shù)的簡化版本,我刪除了一些輔助代碼

function updateClassInstance(current, workInProgress, ctor, newProps, ...) {
    const instance = workInProgress.stateNode;
    const oldProps = workInProgress.memoizedProps;
    instance.props = oldProps;
    if (oldProps !== newProps) {
        callComponentWillReceiveProps(workInProgress, instance, newProps, ...);
    }
    let updateQueue = workInProgress.updateQueue;
    if (updateQueue !== null) {
        processUpdateQueue(workInProgress, updateQueue, ...);
        newState = workInProgress.memoizedState;
    }
    applyDerivedStateFromProps(workInProgress, ...);
    newState = workInProgress.memoizedState;
    const shouldUpdate = checkShouldComponentUpdate(workInProgress, ctor, ...);
    if (shouldUpdate) {
        if (typeof instance.componentWillUpdate === 'function') {
            instance.componentWillUpdate(newProps, newState, nextContext);
        }
        if (typeof instance.componentDidUpdate === 'function') {
          workInProgress.effectTag |= Update;
        }
        if (typeof instance.getSnapshotBeforeUpdate === 'function') {
          workInProgress.effectTag |= Snapshot;
        }
    }
    instance.props = newProps;
    instance.state = newState;
    return shouldUpdate;
}

在調(diào)用生命周期鉤子或添加生命周期鉤子的 effect 之前,React 使用 typeof 檢查實(shí)例是否實(shí)現(xiàn)了相應(yīng)的鉤子。例如:React 使用下面的代碼來檢查實(shí)例是否有 componentDidUpdate 鉤子:

if (typeof instance.componentDidUpdate === 'function') {
    workInProgress.effectTag |= Update;
}

現(xiàn)在我們大概已經(jīng)知道了 ClickCounter 的 fiber 節(jié)點(diǎn)在 render 階段要執(zhí)行的操作,現(xiàn)在讓我們看看這些操作是如何改變 fiber 上的值的。調(diào)用 setState 之后,當(dāng) React 開始工作的時(shí)候,ClickCounter 組件的 fiber 節(jié)點(diǎn)像下面這樣:

{
    effectTag: 0,
    elementType: class ClickCounter,
    firstEffect: null,
    memoizedState: {count: 0},
    type: class ClickCounter,
    stateNode: {
        state: {count: 0}
    },
    updateQueue: {
        baseState: {count: 0},
        firstUpdate: {
            next: {
                payload: (state, props) => {…}
            }
        },
        ...
    }
}

當(dāng)工作完成之后,我們最終得到的 fiber 節(jié)點(diǎn)像這樣:

{
    effectTag: 4,
    elementType: class ClickCounter,
    firstEffect: null,
    memoizedState: {count: 1},
    type: class ClickCounter,
    stateNode: {
        state: {count: 1}
    },
    updateQueue: {
        baseState: {count: 1},
        firstUpdate: null,
        ...
    }
}

對比 ClickCounter fiber 的前后差異我們可以發(fā)現(xiàn)當(dāng) update 被應(yīng)用之后 memoizedState 和 updateQueue.baseState 中的 count 的值為 1。組件實(shí)例中的 state 也會(huì)被更新。在這個(gè)時(shí)候,在隊(duì)列中已經(jīng)沒有 updates 了,所以 firstUpdate 為 null。effectTag 的值不再是 0,它變成了 4,在二進(jìn)制中,這是 100,這代表了 side-effect 的類型是 Update。

export const Update = 0b00000000100;

總結(jié)一下,在處理 ClickCounter fiber 節(jié)點(diǎn)時(shí),React 會(huì)調(diào)用 pre-mutation 生命周期方法、更新 state 以及定義相關(guān)的 side-effects。

Reconciling children for the ClickCounter Fiber

updateClassInstance 運(yùn)行結(jié)束之后,React 會(huì)調(diào)用finishClassComponent函數(shù),在這個(gè)函數(shù)中會(huì)調(diào)用組件的 render 方法,并且在 render 方法返回的 react elements 上運(yùn)行 diff 算法。diff 算法大概的規(guī)則是:

當(dāng)比較兩個(gè)相同類型的React DOM element 時(shí),React 會(huì)檢查這兩個(gè)元素的屬性,保持相同的底層 DOM 節(jié)點(diǎn),只更新已更改的屬性。

Child reconciliation 的過程非常復(fù)雜,如果有可能我會(huì)單獨(dú)寫一篇文章介紹這個(gè)過程。在我們的例子中 ClickCounter 的 render 方法返回的是數(shù)組,所以在 Child reconciliation 時(shí)會(huì)調(diào)用reconcileChildrenArray。

在這里我們有兩點(diǎn)需要著重理解一下

  • 在進(jìn)行 child reconciliation 時(shí) ,它會(huì)為 render 方法返回的 React elements 創(chuàng)建或更新 fiber 節(jié)點(diǎn)。finishClassComponent 返回當(dāng)前 fiber 的第一個(gè) child,這個(gè)返回值會(huì)被賦給 nextUnitOfWork 變量并且在之后的 work loop 中處理。
  • React 會(huì)更新 children 的 props,這是 parent 工作的一部分。

例如,在 React reconciles ClickCounter fiber 的 children 之前,span 元素的 fiber 節(jié)點(diǎn)看上去是這樣的

{
    stateNode: new HTMLSpanElement,
    type: "span",
    key: "2",
    memoizedProps: {children: 0},
    pendingProps: {children: 0},
    ...
}

memoizedProps.children 和 pendingProps.children 的值都是 0。從 render 方法中返回的 span 元素的結(jié)構(gòu)如下:

{
    $$typeof: Symbol(react.element)
    key: "2"
    props: {children: 1}
    ref: null
    type: "span"
}

對比 span fiber 節(jié)點(diǎn)和 span 元素上的屬性,你會(huì)發(fā)現(xiàn)有些屬性值是不同的。createWorkInProgress函數(shù)用于創(chuàng)建 fiber 節(jié)點(diǎn)的 alternate,它使用 react element 上最新的 props 和已經(jīng)存在的 fiber 創(chuàng)建出 alternate。當(dāng) ClickCounter 組件完成 children reconciliation 過程之后,span 的 fiber 節(jié)點(diǎn)的 pendingProps 屬性會(huì)被更新

{
    stateNode: new HTMLSpanElement,
    type: "span",
    key: "2",
    memoizedProps: {children: 0},
    pendingProps: {children: 1},
    ...
}

稍后,當(dāng) react 為 span fiber 執(zhí)行工作時(shí),react 會(huì)將 pendingProps 復(fù)制到 memoizedProps 上并添加更新 DOM 的 effects。

我們已經(jīng)介紹了 React 在 render 階段為 ClickCounter fiber 節(jié)點(diǎn)執(zhí)行的所有工作。由于按鈕是 ClickCounter 組件的第一個(gè) child,所以它將被分配給 nextUnitOfWork 變量,但是按鈕上沒有需要執(zhí)行工作,所以 React 會(huì)快速的移動(dòng)到按鈕的兄弟節(jié)點(diǎn)上,也就是 span fiber 節(jié)點(diǎn),這個(gè)過程發(fā)生在 completeUnitOfWork 函數(shù)中。

處理 Span Fiber 的 update

現(xiàn)在 nextUnitOfWork 中保存的是 span fiber 的 alternate 并且 React 會(huì)在它上面開始工作。React 從 beginWork 函數(shù)開始,這與處理 ClickCounter 的步驟類似。

因?yàn)?span fiber 的類型是 HostComponent,所以在 beginWork 函數(shù)中會(huì)進(jìn)入 HostComponent 對應(yīng)的 switch 分支

function beginWork(current$$1, workInProgress, ...) {
    ...
    switch (workInProgress.tag) {
        case FunctionalComponent: {...}
        case ClassComponent: {...}
        case HostComponent:
          return updateHostComponent(current, workInProgress, ...);
        case ...
}

Reconciling children for the span fiber

在我們的例子中,調(diào)用 updateHostComponent 函數(shù)時(shí),span fiber 沒有發(fā)生任何重要的改變。

只要 beginWork 函數(shù)執(zhí)行完,React 就會(huì)開始執(zhí)行 completeWork 函數(shù),但是在執(zhí)行 completeWork 之前 React 會(huì)更新 span fiber 上的 memoizedProps,在前面的章節(jié),我提到過在 reconciles children for ClickCounter 時(shí),React 更新了 span fiber 上的 pendingProps,只要 span fiber 在 beginWork 中執(zhí)行完成,React 會(huì)將 pendingProps 更新到 memoizedProps 上

function performUnitOfWork(workInProgress) {
    ...
    next = beginWork(current, workInProgress, nextRenderExpirationTime);
    workInProgress.memoizedProps = workInProgress.pendingProps;
    ...
}

在此之后會(huì)調(diào)用 completeWork,completeWork 函數(shù)中是一個(gè)大的 switch 語句,由于 span fiber 是 HostComponent,所以會(huì)進(jìn)入 updateHostComponent 函數(shù):

function completeWork(current, workInProgress, ...) {
    ...
    switch (workInProgress.tag) {
        case FunctionComponent: {...}
        case ClassComponent: {...}
        case HostComponent: {
            ...
            updateHostComponent(current, workInProgress, ...);
        }
        case ...
    }
}

在 updateHostComponent 函數(shù)中,React 基本上執(zhí)行了如下的操作:

  • 準(zhǔn)備 DOM 更新
  • 將 DOM 更新添加到 span fiber 的 updateQueue 中
  • 添加更新 DOM 的 effect

在執(zhí)行這些操作之前,span fiber 看上去是這樣的:

{
    stateNode: new HTMLSpanElement,
    type: "span",
    effectTag: 0
    updateQueue: null
    ...
}

執(zhí)行這些操作之后,span fiber 是這樣的:

{
    stateNode: new HTMLSpanElement,
    type: "span",
    effectTag: 4,
    updateQueue: ["children", "1"],
    ...
}

注意 effectTag 和 updateQueue 的值發(fā)生了變化。effectTag 的值從 0 變成了 4,在二進(jìn)制中,這是 100,這代表了 side-effect 的類型是 Update。updateQueue 字段保存用于 update 的參數(shù)。

只要 React 處理完 ClickCounter 和它的 children,render 階段就結(jié)束了。

Effects list

在我們的例子中,span fiber 和 ClickCounter fiber 有 side effects,React 會(huì)將 HostFiber 的 firstEffect 屬性指向 span fiber。React 在 compliteUnitOfWork函數(shù)中創(chuàng)建 effects list,下面是一個(gè)帶著 effect 的 fiber tree:

帶有 effect 的線性表是:

commit 階段

commit 階段從 completeRoot函數(shù)開始,在開始工作之前先將 FiberRoot.finishedWork 設(shè)置為 null

function completeRoot(
  root: FiberRoot,
  finishedWork: Fiber,
  expirationTime: ExpirationTime,
): void {
    ...
    // Commit the root.
      root.finishedWork = null;
    ...
}

與 render 階段不同的是,commit 階段的操作是同步的。在我們的例子中,在 commit 階段會(huì)更新 DOM 和調(diào)用 componentDidUpdate 生命周期函數(shù),在 render 階段為 span 和 ClickCounter 節(jié)點(diǎn)定義了以下 effect:

{ type: ClickCounter, effectTag: 5 }
{ type: 'span', effectTag: 4 }

ClickCounter 的 effectTag 為 5,在二進(jìn)制中為 101,它表示調(diào)用組件的 componentDidUpdate 生命周期。span 的 effectTag 為 4,在二進(jìn)制中為 100,它表示 DOM 更新

應(yīng)用 effects

讓我們看一下 React 是怎么應(yīng)用(apply)這些 update 的,應(yīng)用 effects 是從調(diào)用commitRoot函數(shù)開始的,這個(gè)函數(shù)主要調(diào)用了如下的三個(gè)函數(shù):

function commitRoot(root, finishedWork) {
    commitBeforeMutationLifecycles()
    commitAllHostEffects();
    root.current = finishedWork;
    commitAllLifeCycles();
}

在 commitRoot 中調(diào)用的這三個(gè)函數(shù)都實(shí)現(xiàn)了一個(gè)大的循環(huán),循環(huán)遍歷 effects list 并檢查 effect 的類型。當(dāng)發(fā)現(xiàn)與函數(shù)的用途有關(guān)的 effect 時(shí),函數(shù)就會(huì)應(yīng)用(apply)它。

由于 commitBeforeMutationLifecycles 的目的是檢查 Snapshot effect 并且調(diào)用 getSnapshotBeforeUpdate 方法,但是我們沒有在 ClickCounter 組件上實(shí)現(xiàn)這個(gè)方法,那么在 render 階段就不會(huì)添加 Snapshot effect,所以在我們的例子中 commitBeforeMutationLifecycles 什么都不會(huì)做。

effect 類型有:

export const NoEffect = /*              */ 0b00000000000;
export const PerformedWork = /*         */ 0b00000000001;
// You can change the rest (and add more).
export const Placement = /*             */ 0b00000000010;
export const Update = /*                */ 0b00000000100;
export const PlacementAndUpdate = /*    */ 0b00000000110;
export const Deletion = /*              */ 0b00000001000;
export const ContentReset = /*          */ 0b00000010000;
export const Callback = /*              */ 0b00000100000;
export const DidCapture = /*            */ 0b00001000000;
export const Ref = /*                   */ 0b00010000000;
export const Snapshot = /*              */ 0b00100000000;
// Update & Callback & Ref & Snapshot
export const LifecycleEffectMask = /*   */ 0b00110100100;
// Union of all host effects
export const HostEffectMask = /*        */ 0b00111111111;
export const Incomplete = /*            */ 0b01000000000;
export const ShouldCapture = /*         */ 0b10000000000;

DOM updates

執(zhí)行完 commitBeforeMutationLifecycles 之后,React 會(huì)調(diào)用 commitAllHostEffects函數(shù) ,在 commitAllHostEffects 中 React 會(huì)將 span 的文本從 0 變成 1,但是對于 ClickCounter fiber,commitAllHostEffects 什么都不會(huì)做,因?yàn)轭惤M件的節(jié)點(diǎn)沒有任何 DOM 更新。commitAllHostEffects 函數(shù)如下:

function commitAllHostEffects() {
    while (nextEffect !== null) {
        ...
        switch (primaryEffectTag) {
          case Placement: {...}
          case PlacementAndUpdate: {...}
          case Update:
            {
              var current = nextEffect.alternate;
              commitWork(current, nextEffect);
              break;
            }
          case Deletion: {...}
        }
        nextEffect = nextEffect.nextEffect;
    }
}

commitAllHostEffects 主要是通過 switch 語句選擇正確的 effect 類型并執(zhí)行相應(yīng)的操作。在本例中,我們需要更新 span 元素上的文本,因此我們在這里進(jìn)入 switch 的 Update 分支。在 Update 分支中調(diào)用 commitWork 函數(shù),但是實(shí)際上最終調(diào)用的是 updateDOMProperties

function updateDOMProperties(domElement, updatePayload, ...) {
  for (let i = 0; i < updatePayload.length; i += 2) {
    const propKey = updatePayload[i];
    const propValue = updatePayload[i + 1];
    if (propKey === STYLE) { ...} 
    else if (propKey === DANGEROUSLY_SET_INNER_HTML) {...} 
    else if (propKey === CHILDREN) {
      setTextContent(domElement, propValue);
    } else {...}
  }
}

updateDOMProperties 接受在 render 階段添加的 updateQueue 作為參數(shù),并且更新 span 元素的 textContent 屬性。

在 commitRoot 函數(shù)中,當(dāng) DOM 更新之后,在 render 階段生成的workInProgress被設(shè)置為current

root.current = finishedWork;

調(diào)用 Post-mutation 生命周期

在 commitRoot 函數(shù)中調(diào)用的最后一個(gè)函數(shù)是 commitAllLifeCycles,React 會(huì)在這個(gè)函數(shù)中調(diào)用 Post-mutation 生命周期函數(shù)。在我們的例子中,在 Render 階段,React 會(huì)將 Update effect 添加到 ClickCounter fiber 上,在 commitAllLifeCycles 函數(shù)中會(huì)檢查 Update effect:

function commitAllLifeCycles(finishedRoot, ...) {
    while (nextEffect !== null) {
        const effectTag = nextEffect.effectTag;
        if (effectTag & (Update | Callback)) {
            const current = nextEffect.alternate;
            commitLifeCycles(finishedRoot, current, nextEffect, ...);
        }
        if (effectTag & Ref) {
            commitAttachRef(nextEffect);
        }
        nextEffect = nextEffect.nextEffect;
    }
}

如果有 ref,在 commitAllLifeCycles 中也會(huì)更新 ref,但是在我們的例子中沒有 ref,所以 commitAttachRef 不會(huì)被調(diào)用。

由于我們 ClickCounter fiber 有 Update effect,所以 commitLifeCycles 會(huì)被調(diào)用,我們定義在組件實(shí)例上的 componentDidUpdate 最終是在 commitLifeCycles 中被調(diào)用的

function commitLifeCycles(finishedRoot, current, ...) {
  ...
  switch (finishedWork.tag) {
    case FunctionComponent: {...}
    case ClassComponent: {
      const instance = finishedWork.stateNode;
      if (finishedWork.effectTag & Update) {
        if (current === null) {
          instance.componentDidMount();
        } else {
          ...
          instance.componentDidUpdate(prevProps, prevState, ...);
        }
      }
    }
    case HostComponent: {...}
    case ...
}

原文鏈接:https://juejin.cn/post/6929854562287222791

欄目分類
最近更新