Reactjs react钩子如何确定它们用于的组件?
我注意到,当我使用react钩子时,子组件的状态更改不会重新引发没有状态更改的父组件。这可以通过以下代码沙盒看到: 由于没有将组件作为参数或绑定上下文传递给钩子,我错误地认为react钩子/状态更改只会触发整个应用程序重新加载程序,比如mithril的工作方式以及react的状态: React递归地遍历树,并在一次勾选期间调用整个更新树的渲染函数 相反,react钩子似乎知道它们与哪个组件关联,因此,渲染引擎只知道更新单个组件,而不调用任何其他组件的Reactjs react钩子如何确定它们用于的组件?,reactjs,react-hooks,Reactjs,React Hooks,我注意到,当我使用react钩子时,子组件的状态更改不会重新引发没有状态更改的父组件。这可以通过以下代码沙盒看到: 由于没有将组件作为参数或绑定上下文传递给钩子,我错误地认为react钩子/状态更改只会触发整个应用程序重新加载程序,比如mithril的工作方式以及react的状态: React递归地遍历树,并在一次勾选期间调用整个更新树的渲染函数 相反,react钩子似乎知道它们与哪个组件关联,因此,渲染引擎只知道更新单个组件,而不调用任何其他组件的render,这与react的设计原则文档上面
render
,这与react的设计原则文档上面所说的相反
render
,而不调用未更改状态的组件的?(在代码沙盒中,尽管子元素的状态发生了更改,但从未调用父元素的渲染
)setInterval
hook所做的那样)答案似乎就在这条线索的某个地方。首先,如果您想从概念上解释钩子是如何工作的,以及它们是如何知道它们绑定到哪个组件实例的,请参见以下内容:
export function useState<S>(initialState: (() => S) | S) {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
useState
hook返回的setter更改时,要重新呈现哪个组件实例。因为这将深入研究React实现的细节,所以随着React实现的发展,其准确性肯定会逐渐降低。在引用React代码的部分内容时,我将删除那些我觉得在回答这个问题时会混淆最相关方面的行
理解其工作原理的第一步是在React中找到相关代码。我将集中讨论三个要点:
- 为组件实例执行呈现逻辑的代码(即,对于功能组件,执行组件功能的代码)
代码useState
- 调用由
useState
useState的组件实例
查找执行渲染逻辑的React代码的一种方法是从render函数中抛出错误。以下对问题的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)
因此,首先我将重点介绍带挂钩的渲染器。这存在于你的内心。如果您想探索到这一点的更多路径,堆栈跟踪中较高的关键点是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如何知道调用与哪个组件实例相关。无论您调用的自定义钩子有多深,它仍然会出现在组件的渲染中(发生在这一行:let children=component(props,refOrContext);
),因此React仍会知道它与渲染之前设置的currentlyRenderingFiber
有关
设置currentlyRenderingFiber
后,它还会设置当前调度程序。请注意,对于组件的初始装载(HooksDispatcherOnMount
)和组件的重新呈现(HooksDispatcherOnUpdate
),调度程序是不同的。我们将在第2部分回到这方面
第2部分在useState
中发生了什么
在中,我们可以找到以下内容:
export function useState<S>(initialState: (() => S) | S) {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
上述mountState
代码中需要注意的重要部分是dispatch
变量。该变量是您状态的设置器,并从末尾的mountState
返回:return[hook.memorizedstate,dispatch]代码>dispatch
只是dispatchAction
函数(也在ReactFiberHooks.js中),它绑定了一些参数,包括currentlyRenderingFiber
和queue
。我们将在第3部分中了解这些函数是如何发挥作用的,但请注意,queue.dispatch
指向相同的dispatch
函数
useState
将更新(重新渲染)案例委托给updateReducer
(也在中)。我有意省略下面的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];
}
您的新状态值将是操作
。由于bind
调用mountState
,光纤和工作队列将自动传递。fiber
(先前保存为表示组件实例的currentlyRenderingFiber
的同一对象)将指向名为useState
的同一组件实例,允许React在您为该特定组件提供新状态值时排队重新呈现该组件
了解React光纤调节器以及光纤是什么的一些额外资源:
- 纤维调节器部分
@Li357如果这是唯一的事情,那么父母的
function dispatchAction<A>(fiber: Fiber, queue: UpdateQueue<A>, action: A)