栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

反应挂钩如何确定它们所要使用的组件?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

反应挂钩如何确定它们所要使用的组件?

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

  • 我写这个答案后发现的深入文章
  • 挂钩常见问题
  • Dan Abramov的相关博客文章

这个问题的目的(如果我正确理解了这个问题的意图)是为了更深入地了解实际实现细节,即当状态通过

useState
钩子返回的设置程序更改时,React如何知道要重新渲染哪个组件实例。因为这将深入研究React实现细节,所以随着React实现的发展,肯定会逐渐变得不那么准确。当引用部分React代码时,我将删除那些我觉得最模糊的方面来回答这个问题。

理解这是如何工作的第一步是在React中找到相关的代码。我将重点介绍三个要点:

  • 执行组件实例的渲染逻辑的代码(即,对于功能组件,执行组件功能的代码)
  • useState
    代码
  • 调用由返回的setter触发的代码
    useState

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

useState

查找执行渲染逻辑的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)

所以首先我将重点关注

renderWithHooks
。这位于ReactFiberHooks中。如果您想探索到这一点的更多路径,那么堆栈跟踪中位于较高位置的关键点是BeginWork和updateFunctionComponent函数,它们都在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
调用与哪个组件实例相关的方式。无论您调用自定义钩子有多深
useState
,它仍然会在组件的渲染中发生(发生在此行:中
letchildren = 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);    }

这将使我们进入ReactFiberHooks中的

useState
函数。对于组件的初始安装与更新(即重新渲染),此映射的映射方式不同。

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
变量。该变量是您状态的设置器,并从
mountState
结尾处返回:
return[hook.memoizedState,dispatch];
dispatch
只是
dispatchAction
函数(也在ReactFiberHooks.js中),绑定了一些参数的函数包括
currentlyRenderingFiber
queue
。我们将在第3部分中研究它们如何发挥作用,但是请注意,这些
queue.dispatch
指向相同的
dispatch
功能。

useState
委托给
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部分 调用由返回的setter时会发生什么

useState

这是dispatchAction的签名:

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

您的新状态值为

action
。该
fiber
工作
queue
将自动由于传递
bind
呼叫
mountState
。将
fiber
(同一个对象作为较早保存
currentlyRenderingFiber
它代表的组件实例)将在同一组件实例指出,呼吁
useState
让反应过来的时候你给它一个新的状态值排队特定组件的重新渲染。

一些其他资源,用于了解反应光纤调节器以及什么是光纤:

  • https://reactjs.org/docs/prebase-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/


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/649769.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号