点击:react git 链接
截止2024.8.22最新版本如下
React hooks
源码好深,hook封装位于packages/react-reconciler/src/ReactFiberHooks.js
hook的数据类型:
export type Hook = {memoizedState: any,baseState: any,baseQueue: Update<any, any> | null,queue: any,next: Hook | null,
};
HooksDispatcher
在 React 18 及更新的版本中,HooksDispatcher
是一个内部使用的调度器,它负责协调 React 组件的挂载(mount)和更新(update)过程中的 Hooks 调用。HooksDispatcherOnMount
和 HooksDispatcherOnUpdate
是 HooksDispatcher
在不同渲染阶段的实例,它们分别用于处理组件首次挂载和随后的更新。
HooksDispatcherOnMount
和 HooksDispatcherOnUpdate
是 React 为了更好地控制组件渲染过程中 Hooks 的行为而引入的内部调度器实例。它们确保了 Hooks 在组件的不同生命周期阶段能够正确地执行。
HooksDispatcherOnMount
HooksDispatcherOnMount
是在组件首次挂载时使用的调度器实例。- 在这个阶段,React 会调用组件内的所有 Hooks,例如
useState
,useEffect
,useContext
等,并且是按照它们在代码中出现的顺序进行调用。 - 由于是首次渲染,
useState
会为每个 state 创建初始状态,useEffect
会执行所有 effect 的逻辑(但不会清除,因为没有之前的 effect)。
HooksDispatcherOnUpdate
HooksDispatcherOnUpdate
是在组件更新时使用的调度器实例。- 在更新阶段,React 同样会调用组件内的所有 Hooks,但这次调用是有条件的。React 会根据调度器来判断 Hooks 是否需要被调用。
- 例如,
useState
会返回当前的状态值,useEffect
会根据 effect 的依赖项来决定是否执行 effect。 - 更新阶段的 Hooks 调用通常涉及到比较前后的状态或 props,以确定是否需要执行某些操作或副作用。
为什么需要不同的调度器实例?
- 性能优化:通过在不同的渲染阶段使用不同的调度器实例,React 可以更精确地控制 Hooks 的行为,从而优化性能。
- 避免副作用的重复执行:在更新阶段,React 需要区分哪些副作用需要重新执行,哪些可以保持不变。
- 保持渲染的一致性:确保组件的渲染行为在不同的渲染阶段保持一致性。
useState解析
useState解析-mountState
mountState就是useState的实现,间接调用了mountStateImpl、mountWorkInProgressHook
。下面展开详细解读
mountWorkInProgressHook
- 拿到当前FiberNode的workInProgressHook变量(可以将其理解为hooks链表的指针),将其指向当前最新hook,并返回引用。
- currentlyRenderingFiber.memoizedState指向hooks链表的头指针。
function mountWorkInProgressHook(): Hook {const hook: Hook = {memoizedState: null,baseState: null,baseQueue: null,queue: null,next: null,};if (workInProgressHook === null) {// This is the first hook in the listcurrentlyRenderingFiber.memoizedState = workInProgressHook = hook;} else {// Append to the end of the listworkInProgressHook = workInProgressHook.next = hook;}return workInProgressHook;
}
通过调用,先将hook挂在到filber node的hooks链表上
计算传入的初始值,并赋值给hook.memoizedState
给hook.queue 赋初值。创建一个新的链表作为更新队列,用来存放更新(setXxx())
function mountStateImpl<S>(initialState: (() => S) | S): Hook {const hook = mountWorkInProgressHook();if (typeof initialState === 'function') {const initialStateInitializer = initialState;// $FlowFixMe[incompatible-use]: Flow doesn't like mixed typesinitialState = initialStateInitializer();if (shouldDoubleInvokeUserFnsInHooksDEV) {setIsStrictModeForDevtools(true);// $FlowFixMe[incompatible-use]: Flow doesn't like mixed typesinitialStateInitializer();setIsStrictModeForDevtools(false);}}hook.memoizedState = hook.baseState = initialState;const queue: UpdateQueue<S, BasicStateAction<S>> = {pending: null,lanes: NoLanes,dispatch: null,lastRenderedReducer: basicStateReducer,lastRenderedState: (initialState: any),};hook.queue = queue;return hook;
}
创建一个 dispatch 示例方法(即 useState 返回的数组的第二个参数:setXxx()),
该方法的作用是用来修改 state,并将此更新添加到更新队列中,通过 .bind 把当前 fiber node、更新队列、 dispatch 方法关联起来:
function mountState<S>(initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {const hook = mountStateImpl(initialState);const queue = hook.queue;const dispatch: Dispatch<BasicStateAction<S>> = (dispatchSetState.bind(null,currentlyRenderingFiber,queue,): any);queue.dispatch = dispatch;return [hook.memoizedState, dispatch];
}
综上,useState 在 Mount (组件初始化)阶段:
- 获取当前 Hook 节点,同时将当前 Hook 添加到 Hook 链表中
- 初始化 Hook 的状态,即读取初始 state 值
- 创建一个新的链表作为更新队列,用来存放更新操作setXxx(),