当前位置: 首页 > 知识库问答 >
问题:

反应钩如何确定它们所用于的组件?

尚嘉勋
2023-03-14

我注意到,当我使用react钩子时,子组件的状态变化不会重新呈现没有状态变化的父组件。下面的代码沙箱可以看到这一点:https://codesandbox.io/s/kmx6nqr4o

由于没有将组件作为参数或绑定上下文传递给钩子,我错误地认为react钩子/状态更改只是触发了整个应用程序重新程序,就像mithril是如何工作的,以及react的设计原则所述:

React递归地遍历树,并在一次滴答中调用整个更新树的呈现函数

当您将useState和setState的用法抽象为自定义钩子函数时,这种关联仍然如何工作?(正如代码沙盒使用setinterval挂钩所做的那样)

似乎答案就在这个线索resolveDispatcher、ReactCurrentOwner、React-Reconciler的某个地方。

共有1个答案

屈昊天
2023-03-14

首先,如果您正在寻找钩子如何工作以及它们如何知道它们绑定到哪个组件实例的概念解释,请参见以下内容:

  • 我写完这个答案后发现的深度文章
  • 钩子常见问题
  • 相关StackOverflow问题
  • 丹·阿布拉莫夫的相关博客文章

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

    null

第1部分:React如何知道名为USESTEST的组件实例?

查找执行呈现html" target="_blank">逻辑的React代码的一种方法是从呈现函数中抛出一个错误。以下对问题的CodeSandbox的修改提供了触发该错误的简单方法:

这为我们提供了以下堆栈跟踪:

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)

以下是最相关的代码:

    currentlyRenderingFiber = workInProgress;
    nextCurrentHook = current !== null ? current.memoizedState : null;
    ReactCurrentDispatcher.current =
      nextCurrentHook === null
        ? HooksDispatcherOnMount
        : HooksDispatcherOnUpdate;
    let children = Component(props, refOrContext);
    currentlyRenderingFiber = null;

CurrentlyRenderingFiber表示正在呈现的组件实例。这就是React知道USESTEST调用与哪个组件实例相关的方法。无论您调用USESTATE的自定义钩子有多深,它仍然会发生在组件的呈现中(发生在这一行:let children=Component(props,refOrContext);),因此React仍然知道它绑定到呈现之前的CurrentlyRenderingFiber集。

在设置CurrentlyRenderingFiber之后,它还设置了当前调度程序。注意,对于组件的初始装入(HooksDispatcheronMount)和组件的重新呈现(HooksDispatcheronUpdate)来说,调度器是不同的。我们将在第2部分中回到这个方面。

在ReactHooks中,我们可以找到以下内容:

    export function useState<S>(initialState: (() => S) | S) {
      const dispatcher = resolveDispatcher();
      return dispatcher.useState(initialState);
    }

这将使我们了解ReactFiberHooks中的USESTEST函数。对于组件的初始装入和更新(即重新呈现),这是不同的映射。

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代码中需要注意的重要部分是dispate变量。该变量是状态的设置器,从mountstate末尾返回:return[hook.memoizedstate,dispatch];dispatch只是dispatchaction函数(也在reactfiberhooks.js中),它绑定了一些参数,包括currentlyrenderingfiberqueue。我们将在第3部分中讨论这些函数是如何发挥作用的,但是请注意queue.dispatch指向同一个dispatch函数。

    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];
    }

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

下面是DispatchAction的签名:

function dispatchAction<A>(fiber: Fiber, queue: UpdateQueue<A>, action: A)

您的新状态值将是操作。由于mountstate中的bind调用,将自动传递纤维和工作队列fiber(与前面保存的CurrentlyRenderingFiber相同的对象,它表示组件实例)将指向调用USESTEST的同一个组件实例,允许React在为该特定组件赋予新的状态值时排队重新呈现该组件。

    null
 类似资料:
  • 问题内容: 我注意到当我使用react挂钩时,子组件的状态更改不会重新呈现没有状态更改的父组件。可以通过以下代码沙箱查看: https //codesandbox.io/s/kmx6nqr4o 由于缺少将组件作为参数或绑定上下文传递给钩子的原因,我错误地认为反应钩子/状态更改只会触发整个应用程序的重新渲染,例如秘银的工作方式以及React的设计原则指出的内容: React递归地遍历树,并在一个滴答

  • 我有一个使用的组件,然后它的输出依赖于上下文中的值。一个简单的例子: 使用react和jest快照中的浅层渲染器测试此组件时。如何更改的值?

  • 我正在尝试使用react钩子创建一个悬停以显示div,我遇到了以下问题: 第69:31行:在函数“renderHideOptionalClauseTrigger”中调用React钩子“useState”,该函数既不是React函数组件,也不是自定义React钩子函数React钩子/钩子规则 搜索关键字以了解有关每个错误的更多信息。 以下是我的代码库:

  • 好吧,问题很简单,但我不知道怎么做。我试着逐个检查两个元素,但这不是最好的方法。我有一组圆形物体。数组有位置字段X和Y。我想遍历数组,检查所有元素是否在一条线上(水平或垂直)。我如何通过数组检查圆心是否在同一条线上。 我试着做这样的事情,看看圆圈是否在同一条垂直线上,但我说它需要一个元素与邻居。

  • 在这个项目中,我使用了反应钩,这个片段用来改变项目的颜色主题,但有一个问题,我无法解决。常量lightTheme={...} Lorem ipsum dolor sit amet,consectetur adipiscing elit,sed do eusmod tempor incidunt ut labore et dolore magna aliqua. 我的减速器:

  • 我正在测试新的React钩子,我遇到了一个我无法修复(也无法理解)的行为。基本上,我有我的函数组件,在它里面有一个函数,我在其中设置钩子。这个函数被传递给一个呈现的组件,并使用props从最后一个呈现的组件调用它。太糟糕了,父级的钩子没有相应地更新! 我知道这似乎很难理解,但我在这里复制了这个错误https://codesandbox.io/s/vvwp33l7o5 我不知道,似乎我不能从组件外部