reactソースは3 udateとudateQueを読みます.


react-domのフォローアップコンタイナー部分です.Reactパッケージのソースコードのバージョンを読む16.8.6です.
前の章でreact-dom関数の論理は、入ってきたReactコンポーネントにrenderオブジェクトを作成し、それがアプリケーション全体の起点であることを識別するために使用され、多くのアプリケーション更新関連の表示符を持っている.その後、対応するfiberRootfiberノードに作成し、fiberRootオブジェクトは、各fiberが所有するノードであり、更新時間のいくつかの情報、propsおよびstateのいくつかの情報、および関連するノード情報を識別している.ReactElementは、一方向リンクのデータ構造によって互いに連結されている.
この章はReactElementの関数でlegacyRenderSubtreeIntoContainerの関連対象の部分を作成しました.次にfiberの関連するロジックを調べます.まずこの部分のコードを振り返ってみます.
function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>,
  children: ReactNodeList,
  container: DOMContainer,
  //     dom  ,       
  forceHydrate: boolean,
  callback: ?Function,
) {
  // ...    fiber        
  //     render   root  
  if (!root) {
    // ...    fiber        
    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function() {
        const instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    }
    // Initial mount should not be batched.
    unbatchedUpdates(() => {
      updateContainer(children, fiberRoot, parentComponent, callback);
    });
  } else {
    fiberRoot = root._internalRoot;
    //   callback     
    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function() {
        const instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    }
    // Update
    updateContainer(children, fiberRoot, parentComponent, callback);
  }
  return getPublicRootInstance(fiberRoot);
}
updateContainerの関数においてcallbackは、入ってきた第3のパラメータであり、renderによれば、コンポーネントがレンダリングされたり更新されたりした後に実行され、また、矢印以外の関数の場合には、callback陰影のコンポーネントを指す.まず、この入参の使い方を振り返ってみます.
const instance = render(
  <Hello text="123" />,
  document.getElementById("page"),
  function () { console.log(this) }
);

console.log(instance);

/*
this === instance === Hello
Hello {
  isMounted: (...)
  replaceState: (...)
  props: {text: "123"}
  context: {}
  refs: {}
  updater: {isMounted: ƒ, enqueueSetState: ƒ, enqueueReplaceState: ƒ, enqueueForceUpdate: ƒ}
  _reactInternalFiber: FiberNode {tag: 1, key: null, stateNode: Hello, elementType: ƒ, type: ƒ, …}
  _reactInternalInstance: {_processChildContext: ƒ}
  state: null
  __proto__: Component
}
*/
上記のコードにreact 関数を使用したとき、renderの第3のエントリとして匿名関数が入ってきました.renderを印刷した後、render関数の戻り値をthis変数に与えてプリントしました.出力されたのはオブジェクト情報であり、renderを使ってinstanceなどのフレームをテストしたことがあるので、このreactに詳しいはずです.これはreact-test-rendererによって開始される完全なコンポーネント情報を示している.
今ソースに戻ります.instanceを呼び出したときに、fiberRootのthisとrenderが返したコンポーネントcallbackの情報は全部renderによって作成されたものです.instanceは、私たちが入ってきたgetPublicRootInstanceを変数reactに与え、新しいcallbackを宣言し、originalCallbackcallbackを作成し、callbackinstanceのthisを指し示してcalloriginalCallbackupdateContainerパラメータに入力した.callbackをどうやって作成するかについては、詳細コードと主流コースは大きくないので、ここでスキップします.この関数がgetPublicRootInstanceに従って情報オブジェクトを提供していることを知っていれば良い.次に、Instance関数が初めて使用されたかどうかにかかわらず、fiberRootInstance方法を呼び出し、違いはrenderを初めて使用した場合、legacyRenderSubtreeIntoContainerupdateContainer方法のコールバックで使用された.renderがしたことは、実際にはupdateContainerが初めて起動したときに、unbatchedUpdatesを大量に更新する必要はなく、この関数がしたことはいくつかのフラグを変更しただけで、すぐにunbatchedUpdatesを更新しました.この部分の論理を省略して、renderの関連部分を重点的に見てみます.
function updateContainer(
  element: ReactNodeList,
  container: OpaqueRoot, // root
  parentComponent: ?React$Component<any, any>, //     null
  callback: ?Function,
): ExpirationTime {
  // fiberRoot current fiberRoot fiber  
  const current = container.current;
  //        js          
  const currentTime = requestCurrentTime();
  //     update   
  const suspenseConfig = requestCurrentSuspenseConfig();
  // @todo     ,      expirationTime
  const expirationTime = computeExpirationForFiber(
    currentTime,
    current,
    suspenseConfig,
  );
  return updateContainerAtExpirationTime(
    element, //    element
    container, // root
    parentComponent, //    null
    expirationTime,
    suspenseConfig,
    callback,
  );
}
 関数では、全体のコード論理が非常に明確に見えます.まずupdateContainer上のCallbackQueueオブジェクト、すなわちupdateContainer上のupdateContainerオブジェクトを取得する.containerに従ってcurrentを取得し、rootFiberが識別するための構成を計算し、Fiberを計算し、requestCurrentTimeを用いてcurrentTimeを更新する.実は、私達はあまり詳細を追究しなくてもいいです.suspenseConfigcomputeExpirationForFiberは何ですか?私たちは簡単にcomputeExpirationForFibercontainerを見に来ました.ここでは変数に関する内容を抜粋してまとめます.
// in react-reconciler/src/ReactFiberExpirationTime.js
export const NoWork = 0;

const NoContext = /*                    */ 0b000000;
const BatchedContext = /*               */ 0b000001;
const EventContext = /*                 */ 0b000010;
const DiscreteEventContext = /*         */ 0b000100;
const LegacyUnbatchedContext = /*       */ 0b001000;
const RenderContext = /*                */ 0b010000;
const CommitContext = /*                */ 0b100000;

// Describes where we are in the React execution stack
let executionContext: number = NoContext;
let currentEventTime: number = NoWork;

//    
function requestCurrentTime() {
  if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
    // We're inside React, so it's fine to read the actual time.
    return msToExpirationTime(now());
  }
  // We're not inside React, so we may be in the middle of a browser event.
  if (currentEventTime !== NoWork) {
    // Use the same start time for all updates until we enter React again.
    return currentEventTime;
  }
  // This is the first update since React yielded. Compute a new start time.
  currentEventTime = msToExpirationTime(now());
  return currentEventTime;
}
 実はすることは簡単で、現在の標識のいくつかの状態によって、対応する時間に戻ります.ここではビット操作に関連して状態を識別する設計パターンがあります.具体的な原理が分かりません.
 の3つのcurrentTime分岐状況は、それぞれReactスケジュールにおいて、ブラウザイベントスケジュールにおいて、および最初の更新で対応する.この中のnow方法はsuspenseConfigに相当すると理解できる.ブラウザ以外のイベントの場合は、現在のタイムスタンプからExpirationTimeを計算して返します.requestCurrentSuspenseConfig具体的に何をしましたか?requestCurrentSuspenseConfigは何ですか?関子を売って、次の専門話requestCurrentTime章まで一緒に見に来ます.
私たちはrequestCurrentTimeのコードを見に行きます.
// shared/ReactSharedInternals.js
import React from 'react';

const ReactSharedInternals =
  React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;

// Prevent newer renderers from RTE when used with older react package versions.
// Current owner and dispatcher used to share the same ref,
// but PR #14548 split them out to better support the react-debug-tools package.
if (!ReactSharedInternals.hasOwnProperty('ReactCurrentDispatcher')) {
  ReactSharedInternals.ReactCurrentDispatcher = {
    current: null,
  };
}
if (!ReactSharedInternals.hasOwnProperty('ReactCurrentBatchConfig')) {
  ReactSharedInternals.ReactCurrentBatchConfig = {
    suspense: null,
  };
}

export default ReactSharedInternals;

// react-reconciler/src/ReactFiberSuspenseConfig.js
const {ReactCurrentBatchConfig} = ReactSharedInternals;

export function requestCurrentSuspenseConfig(): null | SuspenseConfig {
  return ReactCurrentBatchConfig.suspense;
}
  SuspenseはReactが新しく加入した特性で、いくつかの操作が完了したら、またレンダリングすることができます.ifは、後続の動作のために対応する識別子を設定することである.
関数Date.now()の論理的に残っている2つの関数currentEventTimemsToExpirationTimeを見続けます.expirationTimeコードは、実はすべて識別子に基づいて計算されているexpirationTimeであり、ここで関連するいくつかの異なる計算requestCurrentSuspenseConfigの方法は、次の章の専門用語requestCurrentSuspenseConfigの部分にまとめて置いて説明している.
export function computeExpirationForFiber(
  currentTime: ExpirationTime,
  fiber: Fiber,
  suspenseConfig: null | SuspenseConfig,
): ExpirationTime {
  const mode = fiber.mode;
  if ((mode & BatchedMode) === NoMode) {
    return Sync;
  }
  /*
              
           
    ImmediatePriority 99
    UserBlockingPriority 98
    NormalPriority 97
    LowPriority 96
    IdlePriority 95
  */
  const priorityLevel = getCurrentPriorityLevel();
  if ((mode & ConcurrentMode) === NoMode) {
    return priorityLevel === ImmediatePriority ? Sync : Batched;
  }

  if ((executionContext & RenderContext) !== NoContext) {
    // Use whatever time we're already rendering
    return renderExpirationTime;
  }

  let expirationTime;
  if (suspenseConfig !== null) {
    // Compute an expiration time based on the Suspense timeout.
    expirationTime = computeSuspenseExpiration(
      currentTime,
      suspenseConfig.timeoutMs | 0 || LOW_PRIORITY_EXPIRATION,
    );
  } else {
    // Compute an expiration time based on the Scheduler priority.
    switch (priorityLevel) {
      case ImmediatePriority:
        expirationTime = Sync;
        break;
      case UserBlockingPriority:
        // TODO: Rename this to computeUserBlockingExpiration
        expirationTime = computeInteractiveExpiration(currentTime);
        break;
      case NormalPriority:
      case LowPriority: // TODO: Handle LowPriority
        // TODO: Rename this to... something better.
        expirationTime = computeAsyncExpiration(currentTime);
        break;
      case IdlePriority:
        expirationTime = Never;
        break;
      default:
        invariant(false, 'Expected a valid priority level');
    }
  }

  // If we're in the middle of rendering a tree, do not update at the same
  // expiration time that is already rendering.
  // TODO: We shouldn't have to do this if the update is on a different root.
  // Refactor computeExpirationForFiber + scheduleUpdate so we have access to
  // the root when we check for this condition.
  if (workInProgressRoot !== null && expirationTime === renderExpirationTime) {
    // This is a trick to move this update into a separate batch
    expirationTime -= 1;
  }

  return expirationTime;
}

updateContainerを計算した後、computeExpirationForFiberを使用してupdateContainerAtExpirationTimeを更新した.computeExpirationForFiberの部分を呼び出します.
export function updateContainerAtExpirationTime(
  element: ReactNodeList,
  container: OpaqueRoot,
  parentComponent: ?React$Component<any, any>,
  expirationTime: ExpirationTime,
  suspenseConfig: null | SuspenseConfig,
  callback: ?Function,
) {
  // TODO: If this is a nested container, this won't be the root.
  const current = container.current;
  //    context     ...
  return scheduleRootUpdate(
    current,
    element,
    expirationTime,
    suspenseConfig,
    callback,
  );
}

function scheduleRootUpdate(
  current: Fiber, //    Fiber  
  element: ReactNodeList,
  expirationTime: ExpirationTime,
  suspenseConfig: null | SuspenseConfig,
  callback: ?Function,
) {
  // @todo             ,  update  
  const update = createUpdate(expirationTime, suspenseConfig);
  // Caution: React DevTools currently depends on this property
  // being called "element".
  update.payload = {element};

  callback = callback === undefined ? null : callback;
  if (callback !== null) {
    update.callback = callback;
  }
  //       enqueue   
  enqueueUpdate(current, update);
  //         
  scheduleWork(current, expirationTime);

  return expirationTime;
}
ExpirationTime関数のexpirationTime及びexpirationTimeに関する論理を省略した後、残りは直接ExpirationTimeを呼び出して、rootの更新をスケジュールする.ExpirationTimeにおいて、まずcontainerオブジェクトが作成され、updateContainerAtExpirationTimeにこのオブジェクトのパスロード属性が付与される.次に、calbackに対して処理を行い、もしcalbackが存在するなら、updateContainerAtExpirationTimeオブジェクトに値を与えます.enqueueUpdateを使用してcontextオブジェクトを作成します.最後にタスクスケジュールを開始します.この章のコードはDEVを見ています.どのようにスケジュール作業を行うかはこれまでです.スケジュールフローは非常に大きなワークフローで、後にいくつかの章を分割してこの部分のコードを言います.scheduleRootUpdateが現在の識別されているscheduleRootUpdateオブジェクトを更新してdom上にスケジュールするプロセスであるということを知るだけでいいです.次にこのupdateオブジェクトは何ですか?
function createUpdate(
  expirationTime: ExpirationTime,
  suspenseConfig: null | SuspenseConfig,
): Update<*> {
  return {
    expirationTime,
    suspenseConfig,
    /*
      tag  
      export const UpdateState = 0;
      export const ReplaceState = 1;
      export const ForceUpdate = 2;
      //      
      export const CaptureUpdate = 3;
    */
    tag: UpdateState,
    //        ,  element(render      ),setState          
    payload: null,
    callback: null,
    //        
    next: null,
    nextEffect: null,
  };
}
 オブジェクトは、実はelementオブジェクトと同じで、識別子付きのオブジェクトです.これは、updateが現在更新する必要があるコンテンツを識別するために使用されるものである.updateがいくつかありますが、scheduleWorkは次にどれぐらいの内容を更新する必要があるかを示しています.scheduleWorkの関数呼び出し、またはupdateが呼び出したときには、updateを作成してオブジェクトを更新します.updateオブジェクトは互いにnextを介して接続され、一方向チェーンテーブルのデータ構造を形成する.fiberを了解しました.reactが何をしたかもう一度見てみます.
実はupdatereactに入れるプロセスです.renderは、記録setStateを保存するための列である.関数の順序を調整して、updateを一番前に置いて、まず作成したupdateの内容を見てみます.
function createUpdateQueue<State>(baseState: State): UpdateQueue<State> {
  const queue: UpdateQueue<State> = {
    //       state
    baseState,
    //            
    firstUpdate: null,
    lastUpdate: null,
    //        update
    firstCapturedUpdate: null,
    lastCapturedUpdate: null,
    firstEffect: null,
    lastEffect: null,
    firstCapturedEffect: null,
    lastCapturedEffect: null,
  };
  return queue;
}
 もまた、更新が必要なすべてのupdateを維持している識別子のオブジェクトである.enqueueUpdateおよびenqueueUpdateは、それぞれ第1および最後のupdateを指す.updateQueueおよびupdateQueueは、キャプチャされたエラー更新を指すupdateであり、新たに増加したcreateUpdateQueueのためにサービスされる.updateQueueは何ですか?分かりました.UpdateQueueは何をしましたか?
function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
  // Update queues are created lazily.
  // current(fiber  ) workInProcess   
  const alternate = fiber.alternate;
  let queue1;
  let queue2;
  // react dom render      
  if (alternate === null) {
    // There's only one fiber.
    queue1 = fiber.updateQueue;
    queue2 = null;
    if (queue1 === null) {
      queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
    }
  } else {
    // There are two owners.
    queue1 = fiber.updateQueue;
    queue2 = alternate.updateQueue;
    if (queue1 === null) {
      if (queue2 === null) {
        // Neither fiber has an update queue. Create new ones.
        queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
        queue2 = alternate.updateQueue = createUpdateQueue(
          alternate.memoizedState,
        );
      } else {
        // Only one fiber has an update queue. Clone to create a new one.
        queue1 = fiber.updateQueue = cloneUpdateQueue(queue2);
      }
    } else {
      if (queue2 === null) {
        // Only one fiber has an update queue. Clone to create a new one.
        queue2 = alternate.updateQueue = cloneUpdateQueue(queue1);
      } else {
        // Both owners have an update queue.
      }
    }
  }
  //       queue2 null
  if (queue2 === null || queue1 === queue2) {
    // There's only a single queue.
    appendUpdateToQueue(queue1, update);
  } else {
    // There are two queues. We need to append the update to both queues,
    // while accounting for the persistent structure of the list — we don't
    // want the same update to be added multiple times.
    if (queue1.lastUpdate === null || queue2.lastUpdate === null) {
      // One of the queues is not empty. We must add the update to both queues.
      appendUpdateToQueue(queue1, update);
      appendUpdateToQueue(queue2, update);
    } else {
      // Both queues are non-empty. The last update is the same in both lists,
      // because of structural sharing. So, only append to one of the lists.
      appendUpdateToQueue(queue1, update);
      // But we still need to update the `lastUpdate` pointer of queue2.
      queue2.lastUpdate = update;
    }
  }
}

function appendUpdateToQueue<State>(
  queue: UpdateQueue<State>,
  update: Update<State>,
) {
  // Append the update to the end of the list.
  if (queue.lastUpdate === null) {
    // Queue is empty
    queue.firstUpdate = queue.lastUpdate = update;
  } else {
    queue.lastUpdate.next = update;
    queue.lastUpdate = update;
  }
}

//   baseState/firstUpdate/lastUpdate         null
function cloneUpdateQueue<State>(
  currentQueue: UpdateQueue<State>,
): UpdateQueue<State> {
  const queue: UpdateQueue<State> = {
    baseState: currentQueue.baseState,
    firstUpdate: currentQueue.firstUpdate,
    lastUpdate: currentQueue.lastUpdate,

    // TODO: With resuming, if we bail out and resuse the child tree, we should
    // keep these effects.
    firstCapturedUpdate: null,
    lastCapturedUpdate: null,

    firstEffect: null,
    lastEffect: null,

    firstCapturedEffect: null,
    lastCapturedEffect: null,
  };
  return queue;
}
updateの場合によってfirstUpdatelastUpdateとが得られ、updatefirstCapturedUpdatelastCapturedUpdateに追加された.updateは、componentDidCatch関係を識別するために用いられ、UpdateQueue部分を後述するときに言及される.
この章はenqueueUpdate部分の論理を述べ、enqueueUpdateの概念に言及した.これはfiber.alternateがタスクスケジュールを行う根拠であり、異なるスケジューリングレベルによって異なるqueue1を得る.queue2およびUpdateQueueオブジェクトに従って、firstUpdateオブジェクトおよびlastUpdateオブジェクトを維持するalternateキューが作成され、current(fiber ) workInProcess アプリケーション更新の根拠および基礎である.完了したらscheduleWork関数を使ってスケジュール作業ができます.
この章で掘り起こした二つの大きな穴の一つはupdateContainerで、いったい何を表しているのか、どのように計算されているのか、次の章で説明します.ExpirationTimeを説明し終わったら、reactの内容を持ってきます.ExpirationTimeのコード論理とExpirationTimeコード論理構造はとても近いです.完成したら、Fiberに入ります.update全体のスケジュールの章節のソースコードは読みます.