React 源码解读之class组件更新 updateClassComponent (一)

212 阅读3分钟

这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战」。

react 版本:v17.0.3

在任务调度流程的执行任务回调阶段,会调用 ReactFiberBeginWork.new.js 中的 beginWork 函数,根据 workInProgress 的 tag 属性,执行不同类型的React元素的更新函数。

当 workInProgress.tag 的类型为 ClassComponent 时,调用 updateClassComponent 函数对 class组件执行更新操作:

// react-reconciler/src/ReactFiberBeginWork.new.js

function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
): Fiber | null {
 
  // ...

  switch (workInProgress.tag) {

    // ...
    // 调用 updateClassComponent函数 对 class组件进行更新
    case ClassComponent: {
      const Component = workInProgress.type;
      const unresolvedProps = workInProgress.pendingProps;
      const resolvedProps =
        workInProgress.elementType === Component
          ? unresolvedProps
          : resolveDefaultProps(Component, unresolvedProps);
      return updateClassComponent(
        current,
        workInProgress,
        Component,
        resolvedProps,
        renderLanes,
      );
    }

    // ...

  }

  // ...
}

下面,我们来看看 updateClassComponent 这个函数做了什么事情。

updateClassComponent

// react-reconciler/src/ReactFiberBeginWork.new.js

function updateClassComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps: any,
  renderLanes: Lanes,
) {
  
  // ...

  // stateNode 用于记录当前 fiber 所对应的真实 dom 节点或者当前虚拟组件的实例,
  // 这么做的原因第一是为了实现 Ref ,第二是为了实现真实 dom 的跟踪

  // 如果是class组件,fiber的 stateNode属性 存储的是 class组件实例
  // 如果是原生dom标签,fiber 的 stateNode属性 存储的 dom节点
  const instance = workInProgress.stateNode;

  // 用于标识组件是否需要更新
  let shouldUpdate;
  // 情形一:
  // 组件实例还未创建
  if (instance === null) {
    if (current !== null) {
      // A class component without an instance only mounts if it suspended
      // inside a non-concurrent tree, in an inconsistent state. We want to
      // treat it like a new mount, even though an empty version of it already
      // committed. Disconnect the alternate pointers.
      current.alternate = null;
      workInProgress.alternate = null;
      // Since this is conceptually a new fiber, schedule a Placement effect
      workInProgress.flags |= Placement;
    }
    // In the initial pass we might need to construct the instance.
    // 构造 class组件实例
    constructClassInstance(workInProgress, Component, nextProps);
    // 挂载 class组件
    mountClassInstance(workInProgress, Component, nextProps, renderLanes);
    // shouldUpdate 用于标记组件是否需要更新
    // 由于组件是是首次构建,但还未渲染,因此设置为true,标记组件需要更新渲染
    shouldUpdate = true;
  } else if (current === null) {
    // In a resume, we'll already have an instance we can reuse.
    // 情形二:
    // class组件实例已经存在,但是 current 为 null,即 ClassComponent 是初次渲染
    // 那么复用 class组件实例,更新 state/prop,并返回 shouldUpdate
    shouldUpdate = resumeMountClassInstance(
      workInProgress,
      Component,
      nextProps,
      renderLanes,
    );
  } else {
    // 情形三:
    // class组件实例已经创建并且不是初次渲染,
    // 会执行 componentWillUpdate 生命周期函数,返回 shouldUpdate
    shouldUpdate = updateClassInstance(
      current,
      workInProgress,
      Component,
      nextProps,
      renderLanes,
    );
  }

  // 判断是否执行 render 生命周期函数,
  const nextUnitOfWork = finishClassComponent(
    current,
    workInProgress,
    Component,
    shouldUpdate,
    hasContext,
    renderLanes,
  );
 
  // ...

  return nextUnitOfWork;
}

对于 ClassComponent 的更新,有三种情形:

  • 情形一:ClassComponent实例未被创建,此时会调用 constructClassInstance 方法构建class组件实例,然后调用 mountClassInstance 方法挂载class组件,并将 shouldUpdate 置为 true,标记组件需要更新渲染。

  • 情形二:ClassComponent实例已经存在,但current(当前渲染在界面上的fiber树) 为null,即 ClassComponent 是初次渲染,此时调用 resumeMountClassInstance 方法,复用ClassComponent实例,并更新 state/props,然后会执行 componentWillMount 生命周期函数。

  • 情形三:ClassComponent实例已经存在,且已经是多次渲染,此时调用 updateClassInstance 方法执行更新操作,且会执行 componentWillUpdate 生命周期函数。

在以上三种情形中,除了情形一需要强制将 shouldUpdate 置为 true 外,其余的两种情形都是根据执行的结果来设置 shouldUpdate 的值,从而标记组件是否需要更新。

在最后,会执行 finishClassComponent 方法,判断是否需要执行 render 生命周期函数,然后返回下一个需要需要协调的fiber节点,继续执行任务调度流程。

ClassComponent 更新流程

总结

本文介绍了对class组件执行更新的函数updateClassComponent,在该函数中,对class组件的更新分为了三种情形,后续单独对这三种情形做详细解读。