Skip to content

FunctionComponent 组件

函数式组件类型

renderWithHooks

主要是生成首次 mount 和 update 的时候 hooks 的函数方法,从而保证在首次执行的时候可以生成对应的值,在更新的时候可以根据条件去判断是否需要更新

  1. 通过全局变量的方式缓存当前 Hooks 方法函数
  • 初次渲染的时候 使用的是 hook 的 mountState 等方法
  • 更新 的时候 使用的是 hooks 的 updateState 等方法
js
ReactCurrentDispatcher.current =
  current === null || current.memoizedState === null
    ? HooksDispatcherOnMount
    : HooksDispatcherOnUpdate;
  1. 执行函数式组件
js
let children = Component(props, secondArg);
  1. 检查组件渲染期间可能发生的状态更新。

如果为 true ,则表示在组件的渲染阶段发生了状态更新(setState),需要重新渲染组件。 如在函数组件体内部直接调用了 setState。这种情况会导致 React 需要重新渲染组件,以确保状态更新被正确处理。为了防止无限循环,React 会限制重新渲染的次数,最多 25 次,超过就会抛出错误

如何实现

在 hooks 执行的时候,即 dispathAction 的方法中会将全局的 didScheduleRenderPhaseUpdate 设置为 true,从而表示在组件的渲染阶段发生了状态更新。

js
function dispatchAction<A>(
  componentIdentity: Object,
  queue: UpdateQueue<A>,
  action: A
) {
  if (componentIdentity === currentlyRenderingComponent) {
    // This is a render phase update. Stash it in a lazily-created map of
    // queue -> linked list of updates. After this render pass, we'll restart
    // and apply the stashed updates on top of the work-in-progress hook.
    didScheduleRenderPhaseUpdate = true;
  }
}

那么如果在渲染阶段发生了状态更新,那么就可以通过 didScheduleRenderPhaseUpdateDuringThisPass 来判断是否需要重新渲染组件。

js
if (didScheduleRenderPhaseUpdateDuringThisPass) {
  // Keep rendering in a loop for as long as render phase updates continue to
  // be scheduled. Use a counter to prevent infinite loops.
  let numberOfReRenders: number = 0;
  do {
    didScheduleRenderPhaseUpdateDuringThisPass = false;
    localIdCounter = 0;

    if (numberOfReRenders >= RE_RENDER_LIMIT) {
      throw new Error(
        "Too many re-renders. React limits the number of renders to prevent " +
          "an infinite loop."
      );
    }

    numberOfReRenders += 1;

    // Start over from the beginning of the list
    currentHook = null;
    workInProgressHook = null;

    workInProgress.updateQueue = null;

    ReactCurrentDispatcher.current = __DEV__
      ? HooksDispatcherOnRerenderInDEV
      : HooksDispatcherOnRerender;

    children = Component(props, secondArg);
  } while (didScheduleRenderPhaseUpdateDuringThisPass);
}
  1. 重置全局的 hooks 函数方法,防止在非函数式组件中调用了 hooks 方法
js
// 重置 hooks 函数 ,防止其他地方调用 hooks 方法,被污染
ReactCurrentDispatcher.current = ContextOnlyDispatcher;

reconcileChildren

  • mountChildFibers | reconcileChildFibers

这两个方法的核心都是调用 reconcileChildFibers 方法,只是在 mount 阶段的时候 ,会将 shoudTrackEffects 设置为 true,从而在 reconcileChildFibers 方法中会将新的 FiberNode 节点添加到 workInProgress 的 child 链表中。

js
export const reconcileChildFibers = ChildReconciler(true);
export const mountChildFibers = ChildReconciler(false);