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

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

 更新時(shí)間:2022年11月14日 14:42:09   作者:何遇er  
這篇文章主要為大家介紹了React?中state與props更新深入解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

在這篇文章中,我使用下面這樣的應(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)用。

在之前我寫(xiě)了一篇深入介紹 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ù)器端渲染和測(cè)試工具以不同的方式實(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 的值是我們?cè)?ClickCounter 組件中傳遞給 setState 的參數(shù)。它代表在 render 階段需要處理的第一個(gè) update。

處理 ClickCounter Fiber 的 update

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

在調(diào)用 setState 方法之后,React 把我們傳遞給 setState 的參數(shù)添加到 ClickCounter fiber 的 updateQueue 屬性上并且進(jìn)行工作調(diào)度。React 進(jìn)入 render 階段,它使用renderRoot函數(shù)從 fiber 樹(shù)最頂層的 HostRoot 開(kāi)始遍歷 fiber,在遍歷的過(guò)程中 React 會(huì)跳過(guò)已經(jīng)處理完的 fiber 直到遇到?jīng)]有處理的 fiber。在 render 階段,fiber 節(jié)點(diǎn)的所有工作都是在 fiber 的 alternate 字段上進(jìn)行的。如果還沒(méi)有創(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 樹(shù)上的每一個(gè) fiber 都會(huì)執(zhí)行 beginWork 函數(shù),如果你想 debug render 階段,你可以在 beginWork 函數(shù)中打斷點(diǎn)。

beginWork 函數(shù)基本上包含了一個(gè)大的 switch 語(yǔ)句,switch 通過(guò)判斷 fiber 的 tag 來(lái)確定 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 鉤子去確定組件是否需要更新;如果不需要更新就跳過(guò)整個(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ù)的簡(jiǎn)化版本,我刪除了一些輔助代碼

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 使用下面的代碼來(lái)檢查實(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 開(kāi)始工作的時(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,
        ...
    }
}

對(duì)比 ClickCounter fiber 的前后差異我們可以發(fā)現(xiàn)當(dāng) update 被應(yīng)用之后 memoizedState 和 updateQueue.baseState 中的 count 的值為 1。組件實(shí)例中的 state 也會(huì)被更新。在這個(gè)時(shí)候,在隊(duì)列中已經(jīng)沒(méi)有 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 的過(guò)程非常復(fù)雜,如果有可能我會(huì)單獨(dú)寫(xiě)一篇文章介紹這個(gè)過(guò)程。在我們的例子中 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"
}

對(duì)比 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 過(guò)程之后,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 變量,但是按鈕上沒(méi)有需要執(zhí)行工作,所以 React 會(huì)快速的移動(dòng)到按鈕的兄弟節(jié)點(diǎn)上,也就是 span fiber 節(jié)點(diǎn),這個(gè)過(guò)程發(fā)生在 completeUnitOfWork 函數(shù)中。

處理 Span Fiber 的 update

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

因?yàn)?span fiber 的類型是 HostComponent,所以在 beginWork 函數(shù)中會(huì)進(jìn)入 HostComponent 對(duì)應(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 沒(méi)有發(fā)生任何重要的改變。

只要 beginWork 函數(shù)執(zhí)行完,React 就會(huì)開(kāi)始執(zhí)行 completeWork 函數(shù),但是在執(zhí)行 completeWork 之前 React 會(huì)更新 span fiber 上的 memoizedProps,在前面的章節(jié),我提到過(guò)在 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 語(yǔ)句,由于 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ù)開(kāi)始,在開(kāi)始工作之前先將 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ù)開(kāi)始的,這個(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 方法,但是我們沒(méi)有在 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,但是對(duì)于 ClickCounter fiber,commitAllHostEffects 什么都不會(huì)做,因?yàn)轭惤M件的節(jié)點(diǎn)沒(méi)有任何 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 主要是通過(guò) switch 語(yǔ)句選擇正確的 effect 類型并執(zhí)行相應(yīng)的操作。在本例中,我們需要更新 span 元素上的文本,因此我們?cè)谶@里進(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,但是在我們的例子中沒(méi)有 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 ...
}

以上就是React 中state與props更新深入解析的詳細(xì)內(nèi)容,更多關(guān)于React中state props更新的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • React如何使用sortablejs實(shí)現(xiàn)拖拽排序

    React如何使用sortablejs實(shí)現(xiàn)拖拽排序

    這篇文章主要介紹了React如何使用sortablejs實(shí)現(xiàn)拖拽排序問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • react使用.env文件管理全局變量的方法

    react使用.env文件管理全局變量的方法

    本文主要介紹了react使用.env文件管理全局變量的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • 一文詳解React組件API

    一文詳解React組件API

    這篇文章主要介紹了React的組件API,及組件API的用法詳解,文中有詳細(xì)的代碼示例,對(duì)學(xué)習(xí)或工作有一定的參考價(jià)值,感興趣的同學(xué)可以閱讀本文
    2023-04-04
  • react源碼層深入刨析babel解析jsx實(shí)現(xiàn)

    react源碼層深入刨析babel解析jsx實(shí)現(xiàn)

    同作為MVVM框架,React相比于Vue來(lái)講,上手更需要JavaScript功底深厚一些,本系列將閱讀React相關(guān)源碼,從jsx -> VDom -> RDOM等一些列的過(guò)程,將會(huì)在本系列中一一講解
    2022-10-10
  • React使用hook如何實(shí)現(xiàn)數(shù)據(jù)雙向綁定

    React使用hook如何實(shí)現(xiàn)數(shù)據(jù)雙向綁定

    這篇文章主要介紹了React使用hook如何實(shí)現(xiàn)數(shù)據(jù)雙向綁定方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • 解決React?hook?'useState'?cannot?be?called?in?a?class?component報(bào)錯(cuò)

    解決React?hook?'useState'?cannot?be?called?in?

    這篇文章主要為大家介紹了React?hook?'useState'?cannot?be?called?in?a?class?component報(bào)錯(cuò)解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • 使用React實(shí)現(xiàn)一個(gè)簡(jiǎn)單的待辦事項(xiàng)列表的示例代碼

    使用React實(shí)現(xiàn)一個(gè)簡(jiǎn)單的待辦事項(xiàng)列表的示例代碼

    這篇文章我們將詳細(xì)講解如何建立一個(gè)這樣簡(jiǎn)單的列表,文章通過(guò)代碼示例介紹的非常詳細(xì),對(duì)我們的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2023-08-08
  • React配置Redux并結(jié)合本地存儲(chǔ)設(shè)置token方式

    React配置Redux并結(jié)合本地存儲(chǔ)設(shè)置token方式

    這篇文章主要介紹了React配置Redux并結(jié)合本地存儲(chǔ)設(shè)置token方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Unity?RectTransform詳解

    Unity?RectTransform詳解

    unity中的ui元素是有嚴(yán)格的父子關(guān)系的,子物體的位置是根據(jù)父物體的變化而變化的,而子物體和父物體聯(lián)系的橋梁就是Anchor,本文重點(diǎn)介紹Unity?RectTransform的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2024-01-01
  • react-native 封裝視頻播放器react-native-video的使用

    react-native 封裝視頻播放器react-native-video的使用

    本文主要介紹了react-native 封裝視頻播放器react-native-video的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01

最新評(píng)論