Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update & UpdateQueue #54

Open
Cosen95 opened this issue Oct 27, 2020 · 0 comments
Open

Update & UpdateQueue #54

Cosen95 opened this issue Oct 27, 2020 · 0 comments

Comments

@Cosen95
Copy link
Owner

Cosen95 commented Oct 27, 2020

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的操作。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant