React 源码讲解第 5 节- Update 对象和 UpdateQueue 对象

React 源码讲解第 5 节- Update 对象


根据第三节和第四节的内容,创建了 RootFiber 对象和 FiberRoot 对象之后,接下来就是处理更新。

源码

function updateContainer(element, container, parentComponent, callback) {
    
    
  {
    
    
    onScheduleRoot(container, element);
  }

  var current$1 = container.current;
  var currentTime = requestCurrentTimeForUpdate();

  {
    
    
    // $FlowExpectedError - jest isn't a global, and isn't recognized outside of tests
    if ('undefined' !== typeof jest) {
    
    
      warnIfUnmockedScheduler(current$1);
      warnIfNotScopedWithMatchingAct(current$1);
    }
  }

  var suspenseConfig = requestCurrentSuspenseConfig();
  var expirationTime = computeExpirationForFiber(currentTime, current$1, suspenseConfig);
  var context = getContextForSubtree(parentComponent);

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

  {
    
    
    if (isRendering && current !== null && !didWarnAboutNestedUpdates) {
    
    
      didWarnAboutNestedUpdates = true;

      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(current.type) || 'Unknown');
    }
  }

  var update = createUpdate(expirationTime, suspenseConfig); // Caution: React DevTools currently depends on this property
  // being called "element".

  update.payload = {
    
    
    element: element
  };
  callback = callback === undefined ? null : callback;

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

    update.callback = callback;
  }

  enqueueUpdate(current$1, update);
  scheduleWork(current$1, expirationTime);
  return expirationTime;
}

updateContainervar update = createUpdate(expirationTime, suspenseConfig) 提炼出 update 对象。

Update 数据结构

fiber reconciler 将 fiber 的状态更新抽象为 Update 单向链表。

function createUpdate(expirationTime, suspenseConfig) {
    
    
  var update = {
    
    
    // 更新的过期时间
    expirationTime: expirationTime,
    // suspense 配置
    suspenseConfig: suspenseConfig,
    // 更新类型,见如下Update 的四种类型
    tag: UpdateState,
    // 更新内容,比如`setState`接收的第一个参数
    payload: null,
    // 对应的回调,`setState`,`render`都有
    callback: null,
    // 指向下一个更新
    next: null
  };
  update.next = update;

  {
    
    
    update.priority = getCurrentPriorityLevel();//获取优先级等级
  }

  return update;
}

Update 的四种类型

export const UpdateState = 0;//基于 prevState 以及 payload 增量更新
export const ReplaceState = 1;//基于 payload 全量更新
export const ForceUpdate = 2;//强制更新,state 值依旧取 prevState,同时 hasForceUpdate 会被置为 true。
export const CaptureUpdate = 3;//将 fiber.effectTag 置为捕获更新

UpdateQueue 数据结构

updateContainerenqueueUpdate(current$1, update) 提炼出 UpdateQueue 对象。

fiber reconciler 使用 UpdateQueue 存储 Update 更新队列。更新队列有两条,baseQueue 执行中的更新队列,pendingQueue(即 shared.pending)待执行的更新队列。因为 Update 表现为环状单向链表,baseQueue、pendingQueue 均存储单向链表的尾节点。丢弃更新作业的实现在于,将 pendingQueue 复制给 baseQueue,丢弃之前的 baseQueue(current fiber 和 work-in-progress fiber 均会重置 baseQueue)。

function enqueueUpdate(fiber, update) {
    
    
  //存储执行中的更新任务 Update 队列,尾节点存储形式
  var updateQueue = fiber.updateQueue;

  if (updateQueue === null) {
    
    
    // Only occurs if the fiber has been unmounted.
    return;
  }
  //以 pending 属性存储待执行的更新任务 Update 队列,尾节点存储形式
  var sharedQueue = updateQueue.shared;
  var pending = sharedQueue.pending;

  if (pending === null) {
    
    
    // 第一次更新,创建一个循环列表
    update.next = update;
  } else {
    
    
    update.next = pending.next;
    pending.next = update;
  }

  sharedQueue.pending = update;

  {
    
    
    if (currentlyProcessingQueue === sharedQueue && !didWarnUpdateInsideUpdate) {
    
    
      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;
    }
  }
}

fiber reconciler 会在挂载组件时调用 initializeUpdateQueue 函数初始化 fiber 节点的 updateQueue 队列。只有挂载的组件才会有有效的状态更新,卸载的组件没必要使用 updateQueue 属性。enqueueUpdate 函数将 update 添加到 pendingQueue 队列中,典型如类组件在 setState 方法调用期间将 update 添加到 pendingQueue 中。

猜你喜欢

转载自blog.csdn.net/weixin_44135121/article/details/108851278
今日推荐