Cosen95/blog

Update & UpdateQueue

Cosen95 opened this issue · 0 comments

Update & UpdateQueue

创建了RootFiber对象和FiberRoot对象之后,接下来就是处理更新。对应updateContainer:

// packages/react-reconciler/src/ReactFiberReconciler.new.js
export function updateContainer(
  element: ReactNodeList,
  container: OpaqueRoot,
  parentComponent: ?React$Component<any, any>,
  callback: ?Function
): Lane {
  if (__DEV__) {
    onScheduleRoot(container, element);
  }
  const current = container.current;
  const eventTime = requestEventTime();
  if (__DEV__) {
    // $FlowExpectedError - jest isn't a global, and isn't recognized outside of tests
    if ("undefined" !== typeof jest) {
      warnIfUnmockedScheduler(current);
      warnIfNotScopedWithMatchingAct(current);
    }
  }
  const lane = requestUpdateLane(current);

  if (enableSchedulingProfiler) {
    markRenderScheduled(lane);
  }

  const context = getContextForSubtree(parentComponent);
  if (container.context === null) {
    container.context = context;
  } else {
    container.pendingContext = context;
  }

  if (__DEV__) {
    if (
      ReactCurrentFiberIsRendering &&
      ReactCurrentFiberCurrent !== null &&
      !didWarnAboutNestedUpdates
    ) {
      didWarnAboutNestedUpdates = true;
      console.error(
        "Render methods should be a pure function of props and state; " +
          "triggering nested component updates from render is not allowed. " +
          "If necessary, trigger nested updates in componentDidUpdate.\n\n" +
          "Check the render method of %s.",
        getComponentName(ReactCurrentFiberCurrent.type) || "Unknown"
      );
    }
  }

  const update = createUpdate(eventTime, lane);
  // Caution: React DevTools currently depends on this property
  // being called "element".
  update.payload = { element };

  callback = callback === undefined ? null : callback;
  if (callback !== null) {
    if (__DEV__) {
      if (typeof callback !== "function") {
        console.error(
          "render(...): Expected the last optional `callback` argument to be a " +
            "function. Instead received: %s.",
          callback
        );
      }
    }
    update.callback = callback;
  }

  enqueueUpdate(current, update);
  scheduleUpdateOnFiber(current, lane, eventTime);

  return lane;
}

可以看到通过createUpdate()函数创建出一个update对象。

首先需要明白Update对象到底是什么?它主要用于记录组件状态的改变,存放在UpdateQueue中,通过计算得到一个最终的组件更新状态。同时多个Update状态是可以同时存在的。

下面是createUpdate函数所做的事情,其实就是包装了一层转换成了其他数据结构返回。

export function createUpdate(eventTime: number, lane: Lane): Update<*> {
  const update: Update<*> = {
    eventTime,
    lane,

    tag: UpdateState,
    payload: null,
    callback: null,

    next: null,
  };
  return update;
}

其中tag属性对应四种情况,根据不同的情况执行不同的动作:

// packages/react-reconciler/src/ReactUpdateQueue.new.js
export const UpdateState = 0;
export const ReplaceState = 1;
export const ForceUpdate = 2;
export const CaptureUpdate = 3; // 捕获渲染错误时会生成一个Update

这里顺便来看下Update对象:

export type Update<State> = {|
  // TODO: Temporary field. Will remove this by storing a map of
  // transition -> event time on the root.
  // 更新的过期时间
  eventTime: number,
  lane: Lane,
  // export const UpdateState = 0;
  // export const ReplaceState = 1;
  // export const ForceUpdate = 2;
  // export const CaptureUpdate = 3;
  // 指定更新的类型,值为以上几种
  tag: 0 | 1 | 2 | 3,
  // 更新内容,比如`setState`接收的第一个参数
  payload: any,
  // 对应的回调,`setState`,`render`都有
  callback: (() => mixed) | null,
  // 指向下一个更新
  next: Update<State> | null,
|};

创建了Update对象后,将Update对象传入enqueueUpdate(),下面是enqueueUpdate()所做的事情:

export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
  // 存储执行中的更新任务 Update 队列,尾节点存储形式
  const updateQueue = fiber.updateQueue;
  if (updateQueue === null) {
    // Only occurs if the fiber has been unmounted.
    return;
  }
  //以 pending 属性存储待执行的更新任务 Update 队列,尾节点存储形式
  const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
  const pending = sharedQueue.pending;
  if (pending === null) {
    // This is the first update. Create a circular list.
    // 第一次更新,创建一个循环列表
    update.next = update;
  } else {
    update.next = pending.next;
    pending.next = update;
  }
  sharedQueue.pending = update;

  if (__DEV__) {
    if (
      currentlyProcessingQueue === sharedQueue &&
      !didWarnUpdateInsideUpdate
    ) {
      console.error(
        "An update (setState, replaceState, or forceUpdate) was scheduled " +
          "from inside an update function. Update functions should be pure, " +
          "with zero side-effects. Consider using componentDidUpdate or a " +
          "callback."
      );
      didWarnUpdateInsideUpdate = true;
    }
  }
}

由上可以看出enqueueUpdate()函数主要是进行创建和更新UpdateQueue的操作。