【源码】hooks源码实现
hooks源码实现
相关文章
环境
- react:18.2.0
hooks源码实现
一、入口查找
看package.json
可以看到我们的 hook 都是从 ReactClient 文件中导入的
从 ReactHooks 导入
二、hooks 源码实现
useState
1 |
|
- useState 函数接受一个参数 initialState,它可以是一个初始状态值,也可以是一个返回初始状态值的函数。
- resolveDispatcher 函数用于解析当前的 dispatcher,它是一个 React 内部的机制,用于管理和分发 Hook 相关的操作。
- 然后,通过调用 dispatcher.useState(initialState)来获取实际的状态值和状态更新函数。这个调用会委托给特定 dispatcher 中的 useState 函数,它负责实现具体的状态管理逻辑。
- 最后,将获取到的状态值和状态更新函数作为元组的形式返回,其中第一个元素是当前状态值,第二个元素是用于更新状态的函数。
总的来说,useState 函数用于在函数组件中声明和管理状态,并且它通过解析 dispatcher 来委托具体的状态管理逻辑,使得 React 可以支持不同的状态管理方式。
useCallback
1 |
|
- useCallback 函数接受两个参数:callback 表示要记忆的回调函数,deps 表示依赖数组。
- resolveDispatcher 函数用于解析当前的 dispatcher,它是一个 React 内部的机制,用于管理和分发 Hook 相关的操作。
- 然后,通过调用 dispatcher.useCallback(callback, deps)来获取记忆后的回调函数。这个调用会委托给特定 dispatcher 中的 useCallback 函数,它负责实现具体的回调函数记忆逻辑。
- 最后,将记忆后的回调函数直接返回。
总的来说,useCallback 函数用于在函数组件中记忆回调函数,以避免在每次渲染时创建新的回调函数实例。它通过解析 dispatcher 来委托具体的回调函数记忆逻辑,使得 React 可以支持不同的记忆方式。
useMemo
1 |
|
- useMemo 函数接受两个参数:create 表示用于创建 memoized 值的函数,deps 表示依赖数组。
- resolveDispatcher 函数用于解析当前的 dispatcher,它是一个 React 内部的机制,用于管理和分发 Hook 相关的操作。
- 然后,通过调用 dispatcher.useMemo(create, deps)来获取 memoized 的值。这个调用会委托给特定 dispatcher 中的 useMemo 函数,它负责实现具体的 memoization 逻辑。
- 最后,将 memoized 的值直接返回。
总的来说,useMemo 函数用于在函数组件中记忆计算值,以避免在每次渲染时重新计算。它通过解析 dispatcher 来委托具体的 memoization 逻辑,使得 React 可以支持不同的 memoization 策略。
useEffect
1 |
|
- useEffect 函数接受两个参数:create 表示用于创建副作用的函数,deps 表示依赖数组。
- resolveDispatcher 函数用于解析当前的 dispatcher,它是一个 React 内部的机制,用于管理和分发 Hook 相关的操作。
- 然后,通过调用 dispatcher.useEffect(create, deps)来执行副作用函数。这个调用会委托给特定 dispatcher 中的 useEffect 函数,它负责实现具体的副作用逻辑。
- 最后,useEffect 函数没有返回值,因为它主要用于执行副作用操作,而不是返回值。
总的来说,useEffect 函数用于在函数组件中处理副作用,如订阅数据、设置定时器等。它通过解析 dispatcher 来委托具体的副作用逻辑,使得 React 可以支持不同的副作用处理方式。
好吧,相信通过以上几个示例,已经发现了,hooks 都依赖了一个 dispatcher(resolveDispatcher 方法构造出来的)对象,委托它来进行具体的实现,并把它的返回值输出。
三、dispatcher(调度器)是啥
在 React 中,dispatcher 是一个内部机制,用于管理和分发 Hook 相关的操作。它是 React 的核心之一,负责处理函数组件中使用的各种 Hook,如 useState、useEffect、useContext 等。让我详细介绍一下 dispatcher 的功能和作用:
解析 Dispatcher(Resolve Dispatcher):
在 React 内部,使用 resolveDispatcher 函数来获取当前的 dispatcher。这个函数会根据当前环境和配置返回适当的 dispatcher 实例。
具体的 Hook 逻辑(Specific Hook Logic):
每个 dispatcher 都会实现针对不同 Hook 的具体逻辑。例如,useState、useEffect、useMemo 等 Hook 的具体实现都会在 dispatcher 中定义。
Hook 的注册和调用(Registration and Invocation of Hooks):
在函数组件中使用 Hook 时,实际上是通过 dispatcher 来注册和调用 Hook。dispatcher 负责管理 Hook 的状态、副作用以及其他相关逻辑。
状态管理(State Management):
dispatcher 负责管理 Hook 中的状态,例如 useState Hook 中的状态值、useReducer Hook 中的状态和操作等。
副作用处理(Side Effect Handling):
dispatcher 也负责处理副作用,例如 useEffect Hook 中的副作用函数的调用、清除和更新。
扩展性和自定义(Extensibility and Customization):
开发人员可以根据需要扩展或自定义 dispatcher,以支持特定的功能或优化性能。这使得 React 可以根据不同的应用场景和需求进行灵活的定制和优化。
源码实现
1 |
|
可以看到,resolveDispatcher 就是从 ReactCurrentDispatcher 对象中获取当前的 dispatcher 并返回。
那么
ReactCurrentDispatcher
又是啥
四、ReactCurrentDispatcher 又是啥
ReactCurrentDispatcher 是 React 内部的一个重要工具,用于在函数组件中获取当前的 dispatcher 对象,以便在 Hook 相关的操作中使用。它提供了一种方便的方式来访问 React 内部的 Hook 机制,并能够帮助开发者更好地理解和使用 React Hooks。
源码实现
1 |
|
没想到,它竟然是一个这么简单的对象,那么它又是何时赋值的呢
ReactCurrentDispatcher 是在 React 渲染阶段中被赋值的。具体来说,它是在渲染函数组件时被赋值的。
在渲染函数组件之前,React 会将当前正在渲染的组件设置为该函数组件的 Fiber 节点。而在渲染函数组件过程中,React 会将 ReactCurrentDispatcher.current 设置为当前组件对应的 dispatcher,以便在函数组件内部可以通过 ReactCurrentDispatcher.current 来获取当前的 dispatcher 对象。
五、renderWithHooks
我在renderWithHooks方法中找到了具体的复制过程
1 |
|
职责
renderWithHooks 是 React 内部的一个方法,用于在函数组件的渲染过程中执行所有的 Hook 相关逻辑。它负责在渲染函数组件时调用和处理所有的 Hook,包括 useState、useEffect、useContext 等。具体来说,renderWithHooks 主要完成以下几个任务:
初始化 Hook 相关数据结构:在渲染函数组件之前,renderWithHooks 会初始化 Hook 相关的数据结构,包括创建 Fiber Hooks 链表,以及初始化 Hook 相关的上下文。
执行 Hook 相关逻辑:在渲染函数组件过程中,renderWithHooks 会按照 Hook 在代码中的顺序依次执行,调用相应的 Hook 函数,并处理其返回值。它会根据需要创建、更新或销毁 Hook 相关的状态,并保证每次渲染都能正确地执行 Hook 相关的逻辑。
处理更新优先级:renderWithHooks 会根据 Hook 的更新优先级来判断是否需要暂停执行 Hook 相关的逻辑,并根据情况进行调度,以确保渲染过程的顺利进行。
处理异常情况:renderWithHooks 会处理 Hook 相关逻辑中可能出现的异常情况,比如 Hook 的调用顺序不正确、Hook 在条件语句中的使用等,以确保渲染过程的稳定性和健壮性。
总的来说,renderWithHooks 是 React 内部的一个重要方法,负责管理和执行函数组件中所有的 Hook 相关逻辑,是 React Hooks 实现的核心之一。
那么HooksDispatcherOnMount和HooksDispatcherOnUpdate又是什么
HooksDispatcherOnMount 用于处理函数组件的挂载阶段,即在组件初次渲染时执行。HooksDispatcherOnUpdate 用于处理函数组件的更新阶段,即在组件发生重新渲染时执行。
当组件初次渲染时,React 会调用 HooksDispatcherOnMount 来执行函数组件中的所有 Hook 相关逻辑,包括初始化状态、设置副作用、订阅数据等操作。当组件发生重新渲染时,React 会调用 HooksDispatcherOnUpdate 来执行函数组件中的所有 Hook 相关逻辑,包括更新状态、处理副作用、订阅数据等操作。
六、HooksDispatcherOnMount和HooksDispatcherOnUpdate具体实现
源码
1 |
|
果然,他们在这里定义了一系列具体hooks的实现,包括初始化和更新的方法。挑两个看看
mountMemo 与 updateMemo
1 |
|
参数:
- nextCreate: 一个函数,用于计算新的 memoized 值。
- deps: 依赖数组,用于判断 memoized 值是否需要更新。
获取 Hook 对象:
- 首先,通过 mountWorkInProgressHook() 获取当前正在挂载的 Hook 对象。
计算新的 memoized 值:
- 调用 nextCreate() 函数计算新的 memoized 值 nextValue。
开发者工具严格模式:
- 如果开发者工具配置为严格模式 (shouldDoubleInvokeUserFnsInHooksDEV 为 true),则在计算新的 memoized 值前后,会调用两次 nextCreate()。
- 这样做是为了检测 Hooks 函数是否产生了副作用。
更新 Hook 状态:
- 将新的 memoized 值 nextValue 和依赖数组 nextDeps 组成的元组 [nextValue, nextDeps] 赋值给当前 Hook 的 memoizedState 属性。
- 这里使用了 memoizedState 属性来存储 memoized 值和依赖数组,以便后续在组件重新渲染时进行比较。
返回新的 memoized 值:
- 返回新的 memoized 值 nextValue。
1 |
|
参数:
- nextCreate: 一个函数,用于计算新的 memoized 值。
- deps: 依赖数组,用于判断 memoized 值是否需要更新。
获取 Hook 对象:
- 首先,通过 updateWorkInProgressHook() 获取当前的 Hook 对象,该 Hook 对象包含了 memoized 值和其他相关状态。
检查依赖是否发生变化:
- 如果 deps 不为 null,则获取上一次 memoized 值的依赖数组 prevDeps。
- 使用 areHookInputsEqual 函数比较当前的依赖数组 nextDeps 和上一次的依赖数组 prevDeps 是否相等。
- 如果依赖数组相等,则直接返回上一次 memoized 值,表示不需要重新计算。
计算新的 memoized 值:
- 调用 nextCreate() 函数计算新的 memoized 值 nextValue。
开发者工具严格模式:
- 如果开发者工具配置为严格模式 (shouldDoubleInvokeUserFnsInHooksDEV 为 true),则在计算新的 memoized 值前后,会调用两次 nextCreate()。
- 这样做是为了检测 Hooks 函数是否产生了副作用。
更新 Hook 状态:
- 将新的 memoized 值 nextValue 和依赖数组 nextDeps 组成的元组 [nextValue, nextDeps] 赋值给当前 Hook 的 memoizedState 属性。
- 返回新的 memoized 值 nextValue。
mountEffect 与 updateEffect
1 |
|
参数:
- create: 一个函数,用于创建副作用的函数,通常是一个 effect 函数,返回一个清除副作用的函数。
- deps: 依赖数组,用于判断副作用是否需要重新创建或清除。
严格模式检查:
- 如果当前渲染的 Fiber 节点处于严格模式 (StrictEffectsMode),并且不处于无严格模式下的被动副作用模式 (NoStrictPassiveEffectsMode),则将标记设置- 为 MountPassiveDevEffect | PassiveEffect | PassiveStaticEffect,表示副作用是被动的(即不会在每次渲染时触发),并且可能包含开发环境下的被动副作用。
- 否则,将标记设置为 PassiveEffect | PassiveStaticEffect,表示普通的被动副作用。
调用 mountEffectImpl:
- 调用 mountEffectImpl 函数来执行副作用的挂载过程。
- 传入的标记表示副作用的类型,这里根据严格模式的不同情况选择不同的标记。
- 传入 HookPassive 表示这是一个被动副作用。
副作用挂载:
- mountEffectImpl 函数会根据传入的标记来执行副作用的挂载过程,包括创建副作用函数、注册副作用等操作。
- 如果副作用具有依赖数组 (deps),则在副作用的挂载过程中会检查依赖数组是否发生变化,如果发生变化则重新创建副作用。
1 |
|
updateEffect 函数:
- updateEffect 函数接受两个参数:create 和 deps。
- 在函数组件的更新阶段,当调用 useEffect Hook 时,React 会调用 updateEffect 函数来执行副作用的更新逻辑。
- updateEffect 函数会调用内部的 updateEffectImpl 函数来执行副作用的更新过程。
useEffectEventImpl 函数:
- useEffectEventImpl 函数接受一个参数 payload,该参数表示一个事件函数的有效载荷。
- 在函数组件的更新阶段,当存在副作用事件需要处理时,React 会调用 useEffectEventImpl 函数来处理副作用事件。
- useEffectEventImpl 函数会将副作用事件添加到组件更新队列中,以便在组件更新时执行相应的副作用函数。
七、总结
在 React 中,Hooks 是一种用于在函数式组件中添加状态和副作用逻辑的特殊函数。Hooks 是由 Dispatcher 进行管理和执行的。Dispatcher 是 React 内部的一个机制,用于管理 Hooks 的创建、更新和执行。Hooks 和 Dispatcher 之间的关系可以总结如下:
Hooks:
- Hooks 是一组特殊的函数,如 useState、useEffect、useReducer 等,它们允许函数式组件拥有状态和副作用。
- 每个 Hook 函数都会在组件的渲染过程中被调用,并且与特定的组件实例相关联。
Dispatcher:
- Dispatcher 是 React 内部的机制,负责管理 Hooks 的执行。
- Dispatcher 负责跟踪当前正在渲染的组件实例,并负责执行组件中使用的每个 Hook 函数。
- 在组件的渲染过程中,Dispatcher 会按顺序调用每个 Hook 函数,并确保它们按照正确的顺序执行。
- Dispatcher 还负责处理 Hooks 的更新逻辑,例如在组件更新时重新执行具有更新的 Effect Hook。
关系:
- Hooks 是开发者在函数式组件中使用的 API,而 Dispatcher 是 React 内部用于管理和执行 Hooks 的机制。
- 开发者使用 Hooks 来定义组件的状态和副作用逻辑,而 Dispatcher 负责在组件渲染过程中执行这些逻辑,并确保它们按照正确的顺序执行。
- 在 React 应用程序中,开发者通过使用 Hooks 来定义组件的行为,而 Dispatcher 负责在运行时实现这些行为。