gpt4 book ai didi

reactjs - react 钩子(Hook)如何确定它们用于的组件?

转载 作者:行者123 更新时间:2023-12-03 13:10:52 24 4
gpt4 key购买 nike

我注意到当我使用 React 钩子(Hook)时,子组件的状态更改不会重新呈现没有状态更改的父组件。这个代码沙箱可以看到这一点:https://codesandbox.io/s/kmx6nqr4o

由于没有将组件作为参数或绑定(bind)上下文传递给钩子(Hook),我错误地认为 react 钩子(Hook)/状态更改只是触发了整个应用程序的重新渲染,例如 mithril 的工作原理,以及 React 的 Design Principles状态:

React walks the tree recursively and calls render functions of the whole updated tree during a single tick.



相反,似乎 react 钩子(Hook)知道它们关联到哪个组件,因此,渲染引擎知道只更新那个单个组件,而从不调用 render在其他任何方面,都反对 React 的设计原则文档上面所说的。
  • hook和component的关联是怎么做的?
  • 这个关联是如何做到的,让 react 知道只调用 render在状态改变的组件上,而不是那些没有改变的组件? (在代码沙箱中,尽管子元素的状态发生了变化,但从未调用父元素的 render)
  • 当您将 useState 和 setState 的用法抽象为自定义钩子(Hook)函数时,这种关联如何仍然有效? (就像代码沙箱对 setInterval 钩子(Hook)所做的那样)

  • 似乎答案就在这条线索的某个地方 resolveDispatcher , ReactCurrentOwner , react-reconciler .

    最佳答案

    首先,如果您正在寻找有关钩子(Hook)如何工作以及它们如何知道它们绑定(bind)到哪个组件实例的概念性解释,请查看以下内容:

  • in depth article I found after writing this answer
  • Hooks FAQ
  • related StackOverflow question
  • related blog post by Dan Abramov

  • 这个问题的目的(如果我正确理解了问题的意图)是深入了解实际实现细节,即 React 如何通过 useState 返回的 setter 知道在状态更改时重新渲染哪个组件实例。钩。因为这将深入研究 React 实现细节,所以随着 React 实现的发展,它肯定会逐渐变得不那么准确。在引用 React 代码的部分内容时,我将删除我认为混淆了回答这个问题最相关方面的行。

    理解其工作原理的第一步是在 React 中找到相关代码。我将重点关注三个要点:
  • 为组件实例执行渲染逻辑的代码(即对于函数组件,执行组件函数的代码)
  • useState代码
  • 调用 useState 返回的 setter 触发的代码

  • 第 1 部分 React 如何知道调用 useState 的组件实例?

    查找执行渲染逻辑的 React 代码的一种方法是从渲染函数中抛出错误。问题的 CodeSandbox 的以下修改提供了触发该错误的简单方法:

    Edit React hooks parent vs child state

    这为我们提供了以下堆栈跟踪:
    Uncaught Error: Error in child render
    at Child (index.js? [sm]:24)
    at renderWithHooks (react-dom.development.js:15108)
    at updateFunctionComponent (react-dom.development.js:16925)
    at beginWork$1 (react-dom.development.js:18498)
    at HTMLUnknownElement.callCallback (react-dom.development.js:347)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:397)
    at invokeGuardedCallback (react-dom.development.js:454)
    at beginWork$$1 (react-dom.development.js:23217)
    at performUnitOfWork (react-dom.development.js:22208)
    at workLoopSync (react-dom.development.js:22185)
    at renderRoot (react-dom.development.js:21878)
    at runRootCallback (react-dom.development.js:21554)
    at eval (react-dom.development.js:11353)
    at unstable_runWithPriority (scheduler.development.js:643)
    at runWithPriority$2 (react-dom.development.js:11305)
    at flushSyncCallbackQueueImpl (react-dom.development.js:11349)
    at flushSyncCallbackQueue (react-dom.development.js:11338)
    at discreteUpdates$1 (react-dom.development.js:21677)
    at discreteUpdates (react-dom.development.js:2359)
    at dispatchDiscreteEvent (react-dom.development.js:5979)

    所以首先我会关注 renderWithHooks .这位于 ReactFiberHooks 内.如果你想探索更多的路径到这一点,堆栈跟踪中较高的关键点是 beginWorkupdateFunctionComponent ReactFiberBeginWork.js 中的函数。

    这是最相关的代码:
        currentlyRenderingFiber = workInProgress;
    nextCurrentHook = current !== null ? current.memoizedState : null;
    ReactCurrentDispatcher.current =
    nextCurrentHook === null
    ? HooksDispatcherOnMount
    : HooksDispatcherOnUpdate;
    let children = Component(props, refOrContext);
    currentlyRenderingFiber = null;
    currentlyRenderingFiber表示正在呈现的组件实例。这就是 React 如何知道哪个组件实例是 useState调用是相关的。无论您调用自定义钩子(Hook)有多深 useState ,它仍然会出现在你的组件渲染中(发生在这一行: let children = Component(props, refOrContext); ),所以 React 仍然会知道它与 currentlyRenderingFiber 相关联。在渲染之前设置。

    设置后 currentlyRenderingFiber ,它还设置当前的调度程序。请注意,对于组件的初始安装 ( HooksDispatcherOnMount ) 与组件的重新渲染 ( HooksDispatcherOnUpdate ),调度程序是不同的。我们将在第 2 部分中回到这个方面。

    第 2 部分 useState中发生了什么| ?

    ReactHooks我们可以找到以下内容:
        export function useState<S>(initialState: (() => S) | S) {
    const dispatcher = resolveDispatcher();
    return dispatcher.useState(initialState);
    }

    这将使我们进入 useState函数在 ReactFiberHooks .这对于组件的初始安装与更新(即重新渲染)的映射不同。
    const HooksDispatcherOnMount: Dispatcher = {
    useReducer: mountReducer,
    useState: mountState,
    };

    const HooksDispatcherOnUpdate: Dispatcher = {
    useReducer: updateReducer,
    useState: updateState,
    };

    function mountState<S>(
    initialState: (() => S) | S,
    ): [S, Dispatch<BasicStateAction<S>>] {
    const hook = mountWorkInProgressHook();
    if (typeof initialState === 'function') {
    initialState = initialState();
    }
    hook.memoizedState = hook.baseState = initialState;
    const queue = (hook.queue = {
    last: null,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: (initialState: any),
    });
    const dispatch: Dispatch<
    BasicStateAction<S>,
    > = (queue.dispatch = (dispatchAction.bind(
    null,
    // Flow doesn't know this is non-null, but we do.
    ((currentlyRenderingFiber: any): Fiber),
    queue,
    ): any));
    return [hook.memoizedState, dispatch];
    }

    function updateState<S>(
    initialState: (() => S) | S,
    ): [S, Dispatch<BasicStateAction<S>>] {
    return updateReducer(basicStateReducer, (initialState: any));
    }
    mountState中需要注意的重要部分上面的代码是 dispatch多变的。该变量是您的状态的 setter ,并从 mountState 返回最后: return [hook.memoizedState, dispatch]; . dispatch就是 dispatchAction函数(也在 ReactFiberHooks.js 中),绑定(bind)了一些参数,包括 currentlyRenderingFiberqueue .我们将在第 3 部分中了解这些是如何发挥作用的,但请注意 queue.dispatch同样指向 dispatch功能。
    useState委托(delegate)给 updateReducer (也在 ReactFiberHooks 中)用于更新(重新渲染)的情况。我有意省略了 updateReducer 的许多细节下面除了看看它如何处理返回与初始调用相同的 setter。
        function updateReducer<S, I, A>(
    reducer: (S, A) => S,
    initialArg: I,
    init?: I => S,
    ): [S, Dispatch<A>] {
    const hook = updateWorkInProgressHook();
    const queue = hook.queue;
    const dispatch: Dispatch<A> = (queue.dispatch: any);
    return [hook.memoizedState, dispatch];
    }

    你可以在上面看到 queue.dispatch用于在重新渲染时返回相同的 setter。

    第 3 部分 当你调用 useState 返回的 setter 时会发生什么?

    这是 dispatchAction 的签名:
    function dispatchAction<A>(fiber: Fiber, queue: UpdateQueue<A>, action: A)

    您的新状态值将是 action . fiber和工作 queue由于 bind 将自动通过调用 mountState . fiber (之前保存为 currentlyRenderingFiber 的相同对象代表组件实例)将指向调用 useState 的相同组件实例。当你给它一个新的状态值时,允许 React 排队重新渲染该特定组件。

    一些用于理解 React Fiber Reconciler 和光纤的额外资源:
  • https://reactjs.org/docs/codebase-overview.html 的光纤协调器部分
  • https://github.com/acdlite/react-fiber-architecture
  • https://blog.ag-grid.com/index.php/2018/11/29/inside-fiber-in-depth-overview-of-the-new-reconciliation-algorithm-in-react/
  • 关于reactjs - react 钩子(Hook)如何确定它们用于的组件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53974865/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com