源码-hooks如何实现的

2025-3-22 1,418 3/22

需要对react的整个流程了解,可以看下前两篇文章

分为了两个流程,挂载和更新,区分挂载和更新就是依靠的当前处理的fiber节点所映射的另一个fiber节点是否存在

在renderWithHooks里初始化ReactCurrentDispatcher.current,然后再去执行的我们的组件函数,组件函数执行对应的hooks,hooks执行的时候ReactCurrentDispatcher.current.hooks执行

ReactCurrentDispatcher.current =
  current === null || current.memoizedState === null
    ? HooksDispatcherOnMount
    : HooksDispatcherOnUpdate;
currentlyRenderingFiber = workInProgress

HooksDispatcherOnMount(挂载阶段)

挂载阶段 走到mount的逻辑(这里只截取了常用的几个hooks)

const HooksDispatcherOnMount: Dispatcher = {
  useCallback: mountCallback,
  useEffect: mountEffect,
  useImperativeHandle: mountImperativeHandle,
  useLayoutEffect: mountLayoutEffect,
  useMemo: mountMemo,
  useReducer: mountReducer,
  useRef: mountRef,
  useState: mountState,
  useDeferredValue: mountDeferredValue,
  useTransition: mountTransition,
}

这里先看下简单的hooks

useCallback和useMemo

源码-hooks如何实现的

function mountCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
// mountWorkInProgressHook其实就是看是否存在memoizedState,不存在则创建一个hooks对象,存在则直接连接next
  const hook = mountWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
// 然后更新hooks属性
  hook.memoizedState = [callback, nextDeps];
  return callback;
}
function mountWorkInProgressHook(): Hook {
  const hook: Hook = {
    memoizedState: null,
    baseState: null,
    baseQueue: null,
    queue: null,
    next: null,
  };
// 第一个hooks执行的时候workInProgressHook为空了,所以就拿到当前处理的fiber节点,初始化
// 后续hooks的执行只需要通过next连接就行了
  if (workInProgressHook === null) {
// currentlyRenderingFiber = workInProgress; 在renderWithHooks执行的时候就初始化了,指向了当前处理的fiber节点
// 
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  } else {
    workInProgressHook = workInProgressHook.next = hook;
  }
  return workInProgressHook;
}

那么useMemo也差不多,不过区别在于会将回调函数执行,然后存储的是Value值

function mountMemo<T>(
  nextCreate: () => T,
  deps: Array<mixed> | void | null,
): T {
  const hook = mountWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  const nextValue = nextCreate();
  hook.memoizedState = [nextValue, nextDeps];
  return nextValue;
}

更新阶段的处理HooksDispatcherOnUpdate

const HooksDispatcherOnUpdate: Dispatcher = {
  useCallback: updateCallback,
  useContext: readContext,
  useEffect: updateEffect,
  useImperativeHandle: updateImperativeHandle,
  useInsertionEffect: updateInsertionEffect,
  useLayoutEffect: updateLayoutEffect,
  useMemo: updateMemo,
  useReducer: updateReducer,
  useRef: updateRef,
  useState: updateState,
  useDebugValue: updateDebugValue,
  useDeferredValue: updateDeferredValue,
  useTransition: updateTransition,
};

还是以useCallback举例,这里对比新旧deps并没有使用严格等于判断,使用Object.is是为了处理NaN,+0,-0这些情况

function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
  const hook = updateWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
// 这里的是current,也就是当前页面展示的fiber的memoizedState
  const prevState = hook.memoizedState;
  if (prevState !== null) {
    if (nextDeps !== null) {
      const prevDeps: Array<mixed> | null = prevState[1];
      // for循环deps,通过Object.is对比新旧deps,如果依赖没有变 则直接返回了直接的value
      if (areHookInputsEqual(nextDeps, prevDeps)) {
        return prevState[0];
      }
    }
  }
  hook.memoizedState = [callback, nextDeps];
  return callback;
}

updateWorkInProgressHook

因为在renderWithHooks中会先将当前处理的fiber节点的memoizedState重置,所以updateWorkInProgressHook会将当前页面展现的fiber节点的memoizedState赋值给当前处理的fiber节点,然后通过next遍历,返回当前处理hooks

function updateWorkInProgressHook(): Hook {
  let nextCurrentHook: null | Hook;
  if (currentHook === null) {
    const current = currentlyRenderingFiber.alternate;
    if (current !== null) {
      nextCurrentHook = current.memoizedState;
    } else {
      nextCurrentHook = null;
    }
  } else {
    nextCurrentHook = currentHook.next;
  }

  let nextWorkInProgressHook: null | Hook;
  if (workInProgressHook === null) {
    nextWorkInProgressHook = currentlyRenderingFiber.memoizedState;
  } else {
    nextWorkInProgressHook = workInProgressHook.next;
  }

  if (nextWorkInProgressHook !== null) {
    workInProgressHook = nextWorkInProgressHook;
    nextWorkInProgressHook = workInProgressHook.next;
    currentHook = nextCurrentHook;
  } else {
    currentHook = nextCurrentHook;
    const newHook: Hook = {
      memoizedState: currentHook.memoizedState,
      baseState: currentHook.baseState,
      baseQueue: currentHook.baseQueue,
      queue: currentHook.queue,
      next: null,
    };

    if (workInProgressHook === null) {
      currentlyRenderingFiber.memoizedState = workInProgressHook = newHook;
    } else {
      workInProgressHook = workInProgressHook.next = newHook;
    }
  }
  return workInProgressHook;
}

useEffect(mount阶段)

源码-hooks如何实现的

这一步其实也就是生成updateQueue环形链表,然后绑定到当前处理的fiber节点上

源码-hooks如何实现的

useEffect(update阶段)

这里的currentHook是只屏幕前展示的fiber对于的hooks,destory是在执行了对应effect回调函数的时候拿到返回的函数,然后在绑定到对应hooks节点的destory。

源码-hooks如何实现的

useEffect的处理便是在commit刚开始的阶段。最开始就处理的副作用列表,先之前effect链表的destory,然后将回调函数投入到调度队列中去。

源码-hooks如何实现的

useState(mount阶段)

初始化hook结构,赋值memoizedState,创建setStete

源码-hooks如何实现的
useState(update阶段)

update会走useReducer的逻辑

源码-hooks如何实现的

后面补一下setState,这一块挺复杂的

- THE END -
2

非特殊说明,本博所有文章均为博主原创。

共有 0 条评论