useState 源码

分为三个阶段

  • mountState
  • dispatchAction
  • updateState

mountState

我们首先来看源码

export function useState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}

我们接着看 resolveDispatcher,会发现 resolveDispatcher 是 null,回顾 react Fiber 中的流程,在 beginWork 中,有执行 functionComponent 的方法,去那里看

// updateFunctionComponent
renderWithHooks(
      current,
      workInProgress,
      Component,
      nextProps,
      context,
      renderLanes,
);
export function renderWithHooks<Props, SecondArg>(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: (p: Props, arg: SecondArg) => any,
  props: Props,
  secondArg: SecondArg,
  nextRenderLanes: Lanes,
): any {
    // 省略其他代码
    ReactCurrentDispatcher.current =
      current === null || current.memoizedState === null
        ? HooksDispatcherOnMount
        : HooksDispatcherOnUpdate;
}

可以看到,如果是 mount 阶段,返回 HooksDispatcherOnMount,如果是 update 阶段,返回 HooksDispatcherOnUpdate。

  • 返回 HooksDispatcherOnMount,执行 useState
    • 1.执行函数体,得到 state
    • 2.state 存放在 memoizedState 上
    • 3.新建 queue,内部存放 update
    • 4.把 queue 传递给 dispatch
    • 5.返回 dispatch 和默认 state

function mountState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const hook = mountWorkInProgressHook();
  if (typeof initialState === 'function') {
    // $FlowFixMe: Flow doesn't like mixed types
    initialState = initialState();
  }
  hook.memoizedState = hook.baseState = initialState;
  const queue = (hook.queue = {
    pending: null,
    interleaved: null,
    lanes: NoLanes,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: (initialState: any),
  });
  const dispatch: Dispatch<
    BasicStateAction<S>,
  > = (queue.dispatch = (dispatchAction.bind(
    null,
    currentlyRenderingFiber,
    queue,
  ): any));
  return [hook.memoizedState, dispatch];
}

dispatchAction

function dispatchAction<S, A>(
  fiber: Fiber,
  queue: UpdateQueue<S, A>,
  action: A,
) {
    const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
}

执行 dispatchAction,我们可以看到,在 dispatchAction 中,又执行了 scheduleUpdateOnFiber,scheduleUpdateOnFiber 是 scheduler 中的,如果再次执行 scheduleUpdateOnFiber,那么下次执行到 beginWork 中,ReactCurrentDispatcher.current 将不等于 null,则会执行 HooksDispatcherOnUpdate。

当再次执行 HooksDispatcherOnUpdate,触发 updateState。

updateState

function updateState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  return updateReducer(basicStateReducer, (initialState: any));
}

updateReducer 中,判断当前是否有任务存在,如果有,将值放在 eagerState 上,将更改的值赋给 newState

function updateReducer<S, I, A>(
  reducer: (S, A) => S,
  initialArg: I,
  init?: I => S,
): [S, Dispatch<A>] {
    do{
        // ...
        if (update.eagerReducer === reducer) {
          // If this update was processed eagerly, and its reducer matches the
          // current reducer, we can use the eagerly computed state.
          newState = ((update.eagerState: any): S);
        } else {
          const action = update.action;
          newState = reducer(newState, action);
        }
    }while(update !== null && update !== first)
}

useEffect 源码

我们可以看 reactHook.js 中的代码,发现 hooks 调用的方法都几乎相同,只是传参不同。与上面的流程一样,都是 mount、dispatchAction、update

export function useEffect(
  create: () => (() => void) | void,
  deps: Array<mixed> | void | null,
): void {
  const dispatcher = resolveDispatcher();
  return dispatcher.useEffect(create, deps);
}

我们直接看 mountEffect

function mountEffect(
  create: () => (() => void) | void,
  deps: Array<mixed> | void | null,
): void {
    // 非Dev
    return mountEffectImpl(
      PassiveEffect | PassiveStaticEffect,
      HookPassive,
      create,
      deps,
    );
}

执行 mountEffectImpl

function updateEffectImpl(fiberFlags, hookFlags, create, deps): void {
  const hook = updateWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  let destroy = undefined;

  if (currentHook !== null) {
    const prevEffect = currentHook.memoizedState;
    destroy = prevEffect.destroy;
    if (nextDeps !== null) {
      const prevDeps = prevEffect.deps;
      if (areHookInputsEqual(nextDeps, prevDeps)) {
        hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps);
        return;
      }
    }
  }

  currentlyRenderingFiber.flags |= fiberFlags;

  hook.memoizedState = pushEffect(
    HookHasEffect | hookFlags,
    create,
    destroy,
    nextDeps,
  );
}

我们可以看到,将当前的 hookFlag 和 create(function Component)传入 pushEffect 执行,并将返回值赋值给 memoizedState,就是我们在 useState 中提到的初始值,其中,destroy 就是 useEffect 中返回的函数,即 componentWillUnMount

function pushEffect(tag, create, destroy, deps) {
  const effect: Effect = {
    tag,
    create,
    destroy,
    deps,
    // Circular
    next: (null: any),
  };
  let componentUpdateQueue: null | FunctionComponentUpdateQueue = (currentlyRenderingFiber.updateQueue: any);
  if (componentUpdateQueue === null) {
    componentUpdateQueue = createFunctionComponentUpdateQueue();
    currentlyRenderingFiber.updateQueue = (componentUpdateQueue: any);
    componentUpdateQueue.lastEffect = effect.next = effect;
  } else {
    const lastEffect = componentUpdateQueue.lastEffect;
    if (lastEffect === null) {
      componentUpdateQueue.lastEffect = effect.next = effect;
    } else {
      const firstEffect = lastEffect.next;
      lastEffect.next = effect;
      effect.next = firstEffect;
      componentUpdateQueue.lastEffect = effect;
    }
  }
  return effect;
}

看 pushEffect,我们就可以理解,为什么子组件的 useeffect 总是会先执行

之后,我们在 react Render 流程 commit 中,dom 更新完才会执行 useEffects。在源码中,执行 create(),得到返回函数。

最后,在 commitHookEffectListUnmount 中的 safelyCallDestroy 执行 destroy。

function commitHookEffectListUnmount(
  flags: HookFlags,
  finishedWork: Fiber,
  nearestMountedAncestor: Fiber | null,
) {
  const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
  const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
  if (lastEffect !== null) {
    const firstEffect = lastEffect.next;
    let effect = firstEffect;
    do {
      if ((effect.tag & flags) === flags) {
        // Unmount
        const destroy = effect.destroy;
        effect.destroy = undefined;
        if (destroy !== undefined) {
          safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);
        }
      }
      effect = effect.next;
    } while (effect !== firstEffect);
  }
}

useMemo 源码

基于上面的经验,直接看 mountMemo

mount 阶段

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;
}

先执行 mountWorkInProgressHook,在 mountWorkInProgressHook 中,得到 hook 对象,接着,判断是否首次更新,返回缓存好的 workInProgressHook。在 mountMemo 中接着执行传入的 callback,返回 state

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 list
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  } else {
    // Append to the end of the list
    workInProgressHook = workInProgressHook.next = hook;
  }
  return workInProgressHook;
}

update 阶段

在 updateMemo 中,判断上次 useMemo 值不为空,并且 deps 不为空,则进行浅比较。updateWorkInProgressHook 就不看了,还是获得 hook 对象。

function updateMemo<T>(
  nextCreate: () => T,
  deps: Array<mixed> | void | null,
): T {
  const hook = updateWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  const prevState = hook.memoizedState;
  if (prevState !== null) {
    // Assume these are defined. If they're not, areHookInputsEqual will warn.
    if (nextDeps !== null) {
      const prevDeps: Array<mixed> | null = prevState[1];
      if (areHookInputsEqual(nextDeps, prevDeps)) {
        return prevState[0];
      }
    }
  }
  const nextValue = nextCreate();
  hook.memoizedState = [nextValue, nextDeps];
  return nextValue;
}

areHookInputsEqual 中,进行了浅比较,循环 dep 数组,判断 dep 是否变更,如果没有变更,则返回上一次 state 的值。

function areHookInputsEqual(
  nextDeps: Array<mixed>,
  prevDeps: Array<mixed> | null,
) {
    // 去掉无用代码。。。

  for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
    if (is(nextDeps[i], prevDeps[i])) {
      continue;
    }
    return false;
  }
  return true;
}

useCallback 源码

这里就不说了,唯一的区别就是,传入的是 function,且 dep 没有变化,不需要执行 callback。。。

0