Best JavaScript code snippet using playwright-internal
ReactFiberWorkLoop.new.js
Source:ReactFiberWorkLoop.new.js  
...484  checkForNestedUpdates();485  warnAboutRenderPhaseUpdatesInDEV(fiber);486  const root = markUpdateLaneFromFiberToRoot(fiber, lane);487  if (root === null) {488    warnAboutUpdateOnUnmountedFiberInDEV(fiber);489    return null;490  }491  // Mark that the root has a pending update.492  markRootUpdated(root, lane, eventTime);493  if (root === workInProgressRoot) {494    // Received an update to a tree that's in the middle of rendering. Mark495    // that there was an interleaved update work on this root. Unless the496    // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render497    // phase update. In that case, we don't treat render phase updates as if498    // they were interleaved, for backwards compat reasons.499    if (500      deferRenderPhaseUpdateToNextBatch ||501      (executionContext & RenderContext) === NoContext502    ) {503      workInProgressRootUpdatedLanes = mergeLanes(504        workInProgressRootUpdatedLanes,505        lane,506      );507    }508    if (workInProgressRootExitStatus === RootSuspendedWithDelay) {509      // The root already suspended with a delay, which means this render510      // definitely won't finish. Since we have a new update, let's mark it as511      // suspended now, right before marking the incoming update. This has the512      // effect of interrupting the current render and switching to the update.513      // TODO: Make sure this doesn't override pings that happen while we've514      // already started rendering.515      markRootSuspended(root, workInProgressRootRenderLanes);516    }517  }518  // TODO: requestUpdateLanePriority also reads the priority. Pass the519  // priority as an argument to that function and this one.520  const priorityLevel = getCurrentPriorityLevel();521  if (lane === SyncLane) {522    if (523      // Check if we're inside unbatchedUpdates524      (executionContext & LegacyUnbatchedContext) !== NoContext &&525      // Check if we're not already rendering526      (executionContext & (RenderContext | CommitContext)) === NoContext527    ) {528      // Register pending interactions on the root to avoid losing traced interaction data.529      schedulePendingInteractions(root, lane);530      // This is a legacy edge case. The initial mount of a ReactDOM.render-ed531      // root inside of batchedUpdates should be synchronous, but layout updates532      // should be deferred until the end of the batch.533      performSyncWorkOnRoot(root);534    } else {535      ensureRootIsScheduled(root, eventTime);536      schedulePendingInteractions(root, lane);537      if (executionContext === NoContext) {538        // Flush the synchronous work now, unless we're already working or inside539        // a batch. This is intentionally inside scheduleUpdateOnFiber instead of540        // scheduleCallbackForFiber to preserve the ability to schedule a callback541        // without immediately flushing it. We only do this for user-initiated542        // updates, to preserve historical behavior of legacy mode.543        resetRenderTimer();544        flushSyncCallbackQueue();545      }546    }547  } else {548    // Schedule a discrete update but only if it's not Sync.549    if (550      (executionContext & DiscreteEventContext) !== NoContext &&551      // Only updates at user-blocking priority or greater are considered552      // discrete, even inside a discrete event.553      (priorityLevel === UserBlockingSchedulerPriority ||554        priorityLevel === ImmediateSchedulerPriority)555    ) {556      // This is the result of a discrete event. Track the lowest priority557      // discrete update per root so we can flush them early, if needed.558      if (rootsWithPendingDiscreteUpdates === null) {559        rootsWithPendingDiscreteUpdates = new Set([root]);560      } else {561        rootsWithPendingDiscreteUpdates.add(root);562      }563    }564    // Schedule other updates after in case the callback is sync.565    ensureRootIsScheduled(root, eventTime);566    schedulePendingInteractions(root, lane);567  }568  // We use this when assigning a lane for a transition inside569  // `requestUpdateLane`. We assume it's the same as the root being updated,570  // since in the common case of a single root app it probably is. If it's not571  // the same root, then it's not a huge deal, we just might batch more stuff572  // together more than necessary.573  mostRecentlyUpdatedRoot = root;574}575// This is split into a separate function so we can mark a fiber with pending576// work without treating it as a typical update that originates from an event;577// e.g. retrying a Suspense boundary isn't an update, but it does schedule work578// on a fiber.579function markUpdateLaneFromFiberToRoot(580  sourceFiber: Fiber,581  lane: Lane,582): FiberRoot | null {583  // Update the source fiber's lanes584  sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);585  let alternate = sourceFiber.alternate;586  if (alternate !== null) {587    alternate.lanes = mergeLanes(alternate.lanes, lane);588  }589  if (__DEV__) {590    if (591      alternate === null &&592      (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags593    ) {594      warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);595    }596  }597  // Walk the parent path to the root and update the child expiration time.598  let node = sourceFiber;599  let parent = sourceFiber.return;600  while (parent !== null) {601    parent.childLanes = mergeLanes(parent.childLanes, lane);602    alternate = parent.alternate;603    if (alternate !== null) {604      alternate.childLanes = mergeLanes(alternate.childLanes, lane);605    } else {606      if (__DEV__) {607        if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {608          warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);609        }610      }611    }612    node = parent;613    parent = parent.return;614  }615  if (node.tag === HostRoot) {616    const root: FiberRoot = node.stateNode;617    return root;618  } else {619    return null;620  }621}622// Use this function to schedule a task for a root. There's only one task per623// root; if a task was already scheduled, we'll check to make sure the priority624// of the existing task is the same as the priority of the next level that the625// root has work on. This function is called on every update, and right before626// exiting a task.627function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {628  const existingCallbackNode = root.callbackNode;629  // Check if any lanes are being starved by other work. If so, mark them as630  // expired so we know to work on those next.631  markStarvedLanesAsExpired(root, currentTime);632  // Determine the next lanes to work on, and their priority.633  const nextLanes = getNextLanes(634    root,635    root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,636  );637  // This returns the priority level computed during the `getNextLanes` call.638  const newCallbackPriority = returnNextLanesPriority();639  if (nextLanes === NoLanes) {640    // Special case: There's nothing to work on.641    if (existingCallbackNode !== null) {642      cancelCallback(existingCallbackNode);643      root.callbackNode = null;644      root.callbackPriority = NoLanePriority;645    }646    return;647  }648  // Check if there's an existing task. We may be able to reuse it.649  if (existingCallbackNode !== null) {650    const existingCallbackPriority = root.callbackPriority;651    if (existingCallbackPriority === newCallbackPriority) {652      // The priority hasn't changed. We can reuse the existing task. Exit.653      return;654    }655    // The priority changed. Cancel the existing callback. We'll schedule a new656    // one below.657    cancelCallback(existingCallbackNode);658  }659  // Schedule a new callback.660  let newCallbackNode;661  if (newCallbackPriority === SyncLanePriority) {662    // Special case: Sync React callbacks are scheduled on a special663    // internal queue664    newCallbackNode = scheduleSyncCallback(665      performSyncWorkOnRoot.bind(null, root),666    );667  } else if (newCallbackPriority === SyncBatchedLanePriority) {668    newCallbackNode = scheduleCallback(669      ImmediateSchedulerPriority,670      performSyncWorkOnRoot.bind(null, root),671    );672  } else {673    const schedulerPriorityLevel = lanePriorityToSchedulerPriority(674      newCallbackPriority,675    );676    newCallbackNode = scheduleCallback(677      schedulerPriorityLevel,678      performConcurrentWorkOnRoot.bind(null, root),679    );680  }681  root.callbackPriority = newCallbackPriority;682  root.callbackNode = newCallbackNode;683}684// This is the entry point for every concurrent task, i.e. anything that685// goes through Scheduler.686function performConcurrentWorkOnRoot(root) {687  // Since we know we're in a React event, we can clear the current688  // event time. The next update will compute a new event time.689  currentEventTime = NoTimestamp;690  currentEventWipLanes = NoLanes;691  currentEventPendingLanes = NoLanes;692  invariant(693    (executionContext & (RenderContext | CommitContext)) === NoContext,694    'Should not already be working.',695  );696  // Flush any pending passive effects before deciding which lanes to work on,697  // in case they schedule additional work.698  const originalCallbackNode = root.callbackNode;699  const didFlushPassiveEffects = flushPassiveEffects();700  if (didFlushPassiveEffects) {701    // Something in the passive effect phase may have canceled the current task.702    // Check if the task node for this root was changed.703    if (root.callbackNode !== originalCallbackNode) {704      // The current task was canceled. Exit. We don't need to call705      // `ensureRootIsScheduled` because the check above implies either that706      // there's a new task, or that there's no remaining work on this root.707      return null;708    } else {709      // Current task was not canceled. Continue.710    }711  }712  // Determine the next expiration time to work on, using the fields stored713  // on the root.714  let lanes = getNextLanes(715    root,716    root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,717  );718  if (lanes === NoLanes) {719    // Defensive coding. This is never expected to happen.720    return null;721  }722  let exitStatus = renderRootConcurrent(root, lanes);723  if (724    includesSomeLane(725      workInProgressRootIncludedLanes,726      workInProgressRootUpdatedLanes,727    )728  ) {729    // The render included lanes that were updated during the render phase.730    // For example, when unhiding a hidden tree, we include all the lanes731    // that were previously skipped when the tree was hidden. That set of732    // lanes is a superset of the lanes we started rendering with.733    //734    // So we'll throw out the current work and restart.735    prepareFreshStack(root, NoLanes);736  } else if (exitStatus !== RootIncomplete) {737    if (exitStatus === RootErrored) {738      executionContext |= RetryAfterError;739      // If an error occurred during hydration,740      // discard server response and fall back to client side render.741      if (root.hydrate) {742        root.hydrate = false;743        clearContainer(root.containerInfo);744      }745      // If something threw an error, try rendering one more time. We'll render746      // synchronously to block concurrent data mutations, and we'll includes747      // all pending updates are included. If it still fails after the second748      // attempt, we'll give up and commit the resulting tree.749      lanes = getLanesToRetrySynchronouslyOnError(root);750      if (lanes !== NoLanes) {751        exitStatus = renderRootSync(root, lanes);752      }753    }754    if (exitStatus === RootFatalErrored) {755      const fatalError = workInProgressRootFatalError;756      prepareFreshStack(root, NoLanes);757      markRootSuspended(root, lanes);758      ensureRootIsScheduled(root, now());759      throw fatalError;760    }761    // We now have a consistent tree. The next step is either to commit it,762    // or, if something suspended, wait to commit it after a timeout.763    const finishedWork: Fiber = (root.current.alternate: any);764    root.finishedWork = finishedWork;765    root.finishedLanes = lanes;766    finishConcurrentRender(root, exitStatus, lanes);767  }768  ensureRootIsScheduled(root, now());769  if (root.callbackNode === originalCallbackNode) {770    // The task node scheduled for this root is the same one that's771    // currently executed. Need to return a continuation.772    return performConcurrentWorkOnRoot.bind(null, root);773  }774  return null;775}776function finishConcurrentRender(root, exitStatus, lanes) {777  switch (exitStatus) {778    case RootIncomplete:779    case RootFatalErrored: {780      invariant(false, 'Root did not complete. This is a bug in React.');781    }782    // Flow knows about invariant, so it complains if I add a break783    // statement, but eslint doesn't know about invariant, so it complains784    // if I do. eslint-disable-next-line no-fallthrough785    case RootErrored: {786      // We should have already attempted to retry this tree. If we reached787      // this point, it errored again. Commit it.788      commitRoot(root);789      break;790    }791    case RootSuspended: {792      markRootSuspended(root, lanes);793      // We have an acceptable loading state. We need to figure out if we794      // should immediately commit it or wait a bit.795      if (796        includesOnlyRetries(lanes) &&797        // do not delay if we're inside an act() scope798        !shouldForceFlushFallbacksInDEV()799      ) {800        // This render only included retries, no updates. Throttle committing801        // retries so that we don't show too many loading states too quickly.802        const msUntilTimeout =803          globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();804        // Don't bother with a very short suspense time.805        if (msUntilTimeout > 10) {806          const nextLanes = getNextLanes(root, NoLanes);807          if (nextLanes !== NoLanes) {808            // There's additional work on this root.809            break;810          }811          const suspendedLanes = root.suspendedLanes;812          if (!isSubsetOfLanes(suspendedLanes, lanes)) {813            // We should prefer to render the fallback of at the last814            // suspended level. Ping the last suspended level to try815            // rendering it again.816            // FIXME: What if the suspended lanes are Idle? Should not restart.817            const eventTime = requestEventTime();818            markRootPinged(root, suspendedLanes, eventTime);819            break;820          }821          // The render is suspended, it hasn't timed out, and there's no822          // lower priority work to do. Instead of committing the fallback823          // immediately, wait for more data to arrive.824          root.timeoutHandle = scheduleTimeout(825            commitRoot.bind(null, root),826            msUntilTimeout,827          );828          break;829        }830      }831      // The work expired. Commit immediately.832      commitRoot(root);833      break;834    }835    case RootSuspendedWithDelay: {836      markRootSuspended(root, lanes);837      if (includesOnlyTransitions(lanes)) {838        // This is a transition, so we should exit without committing a839        // placeholder and without scheduling a timeout. Delay indefinitely840        // until we receive more data.841        break;842      }843      if (!shouldForceFlushFallbacksInDEV()) {844        // This is not a transition, but we did trigger an avoided state.845        // Schedule a placeholder to display after a short delay, using the Just846        // Noticeable Difference.847        // TODO: Is the JND optimization worth the added complexity? If this is848        // the only reason we track the event time, then probably not.849        // Consider removing.850        const mostRecentEventTime = getMostRecentEventTime(root, lanes);851        const eventTimeMs = mostRecentEventTime;852        const timeElapsedMs = now() - eventTimeMs;853        const msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs;854        // Don't bother with a very short suspense time.855        if (msUntilTimeout > 10) {856          // Instead of committing the fallback immediately, wait for more data857          // to arrive.858          root.timeoutHandle = scheduleTimeout(859            commitRoot.bind(null, root),860            msUntilTimeout,861          );862          break;863        }864      }865      // Commit the placeholder.866      commitRoot(root);867      break;868    }869    case RootCompleted: {870      // The work completed. Ready to commit.871      commitRoot(root);872      break;873    }874    default: {875      invariant(false, 'Unknown root exit status.');876    }877  }878}879function markRootSuspended(root, suspendedLanes) {880  // When suspending, we should always exclude lanes that were pinged or (more881  // rarely, since we try to avoid it) updated during the render phase.882  // TODO: Lol maybe there's a better way to factor this besides this883  // obnoxiously named function :)884  suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);885  suspendedLanes = removeLanes(suspendedLanes, workInProgressRootUpdatedLanes);886  markRootSuspended_dontCallThisOneDirectly(root, suspendedLanes);887}888// This is the entry point for synchronous tasks that don't go889// through Scheduler890function performSyncWorkOnRoot(root) {891  invariant(892    (executionContext & (RenderContext | CommitContext)) === NoContext,893    'Should not already be working.',894  );895  flushPassiveEffects();896  let lanes;897  let exitStatus;898  if (899    root === workInProgressRoot &&900    includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes)901  ) {902    // There's a partial tree, and at least one of its lanes has expired. Finish903    // rendering it before rendering the rest of the expired work.904    lanes = workInProgressRootRenderLanes;905    exitStatus = renderRootSync(root, lanes);906    if (907      includesSomeLane(908        workInProgressRootIncludedLanes,909        workInProgressRootUpdatedLanes,910      )911    ) {912      // The render included lanes that were updated during the render phase.913      // For example, when unhiding a hidden tree, we include all the lanes914      // that were previously skipped when the tree was hidden. That set of915      // lanes is a superset of the lanes we started rendering with.916      //917      // Note that this only happens when part of the tree is rendered918      // concurrently. If the whole tree is rendered synchronously, then there919      // are no interleaved events.920      lanes = getNextLanes(root, lanes);921      exitStatus = renderRootSync(root, lanes);922    }923  } else {924    lanes = getNextLanes(root, NoLanes);925    exitStatus = renderRootSync(root, lanes);926  }927  if (root.tag !== LegacyRoot && exitStatus === RootErrored) {928    executionContext |= RetryAfterError;929    // If an error occurred during hydration,930    // discard server response and fall back to client side render.931    if (root.hydrate) {932      root.hydrate = false;933      clearContainer(root.containerInfo);934    }935    // If something threw an error, try rendering one more time. We'll render936    // synchronously to block concurrent data mutations, and we'll includes937    // all pending updates are included. If it still fails after the second938    // attempt, we'll give up and commit the resulting tree.939    lanes = getLanesToRetrySynchronouslyOnError(root);940    if (lanes !== NoLanes) {941      exitStatus = renderRootSync(root, lanes);942    }943  }944  if (exitStatus === RootFatalErrored) {945    const fatalError = workInProgressRootFatalError;946    prepareFreshStack(root, NoLanes);947    markRootSuspended(root, lanes);948    ensureRootIsScheduled(root, now());949    throw fatalError;950  }951  // We now have a consistent tree. Because this is a sync render, we952  // will commit it even if something suspended.953  const finishedWork: Fiber = (root.current.alternate: any);954  root.finishedWork = finishedWork;955  root.finishedLanes = lanes;956  commitRoot(root);957  // Before exiting, make sure there's a callback scheduled for the next958  // pending level.959  ensureRootIsScheduled(root, now());960  return null;961}962export function flushRoot(root: FiberRoot, lanes: Lanes) {963  markRootExpired(root, lanes);964  ensureRootIsScheduled(root, now());965  if ((executionContext & (RenderContext | CommitContext)) === NoContext) {966    resetRenderTimer();967    flushSyncCallbackQueue();968  }969}970export function getExecutionContext(): ExecutionContext {971  return executionContext;972}973export function flushDiscreteUpdates() {974  // TODO: Should be able to flush inside batchedUpdates, but not inside `act`.975  // However, `act` uses `batchedUpdates`, so there's no way to distinguish976  // those two cases. Need to fix this before exposing flushDiscreteUpdates977  // as a public API.978  if (979    (executionContext & (BatchedContext | RenderContext | CommitContext)) !==980    NoContext981  ) {982    if (__DEV__) {983      if ((executionContext & RenderContext) !== NoContext) {984        console.error(985          'unstable_flushDiscreteUpdates: Cannot flush updates when React is ' +986            'already rendering.',987        );988      }989    }990    // We're already rendering, so we can't synchronously flush pending work.991    // This is probably a nested event dispatch triggered by a lifecycle/effect,992    // like `el.focus()`. Exit.993    return;994  }995  flushPendingDiscreteUpdates();996  // If the discrete updates scheduled passive effects, flush them now so that997  // they fire before the next serial event.998  flushPassiveEffects();999}1000export function deferredUpdates<A>(fn: () => A): A {1001  if (decoupleUpdatePriorityFromScheduler) {1002    const previousLanePriority = getCurrentUpdateLanePriority();1003    try {1004      setCurrentUpdateLanePriority(DefaultLanePriority);1005      return runWithPriority(NormalSchedulerPriority, fn);1006    } finally {1007      setCurrentUpdateLanePriority(previousLanePriority);1008    }1009  } else {1010    return runWithPriority(NormalSchedulerPriority, fn);1011  }1012}1013function flushPendingDiscreteUpdates() {1014  if (rootsWithPendingDiscreteUpdates !== null) {1015    // For each root with pending discrete updates, schedule a callback to1016    // immediately flush them.1017    const roots = rootsWithPendingDiscreteUpdates;1018    rootsWithPendingDiscreteUpdates = null;1019    roots.forEach(root => {1020      markDiscreteUpdatesExpired(root);1021      ensureRootIsScheduled(root, now());1022    });1023  }1024  // Now flush the immediate queue.1025  flushSyncCallbackQueue();1026}1027export function batchedUpdates<A, R>(fn: A => R, a: A): R {1028  const prevExecutionContext = executionContext;1029  executionContext |= BatchedContext;1030  try {1031    return fn(a);1032  } finally {1033    executionContext = prevExecutionContext;1034    if (executionContext === NoContext) {1035      // Flush the immediate callbacks that were scheduled during this batch1036      resetRenderTimer();1037      flushSyncCallbackQueue();1038    }1039  }1040}1041export function batchedEventUpdates<A, R>(fn: A => R, a: A): R {1042  const prevExecutionContext = executionContext;1043  executionContext |= EventContext;1044  try {1045    return fn(a);1046  } finally {1047    executionContext = prevExecutionContext;1048    if (executionContext === NoContext) {1049      // Flush the immediate callbacks that were scheduled during this batch1050      resetRenderTimer();1051      flushSyncCallbackQueue();1052    }1053  }1054}1055export function discreteUpdates<A, B, C, D, R>(1056  fn: (A, B, C) => R,1057  a: A,1058  b: B,1059  c: C,1060  d: D,1061): R {1062  const prevExecutionContext = executionContext;1063  executionContext |= DiscreteEventContext;1064  if (decoupleUpdatePriorityFromScheduler) {1065    const previousLanePriority = getCurrentUpdateLanePriority();1066    try {1067      setCurrentUpdateLanePriority(InputDiscreteLanePriority);1068      return runWithPriority(1069        UserBlockingSchedulerPriority,1070        fn.bind(null, a, b, c, d),1071      );1072    } finally {1073      setCurrentUpdateLanePriority(previousLanePriority);1074      executionContext = prevExecutionContext;1075      if (executionContext === NoContext) {1076        // Flush the immediate callbacks that were scheduled during this batch1077        resetRenderTimer();1078        flushSyncCallbackQueue();1079      }1080    }1081  } else {1082    try {1083      return runWithPriority(1084        UserBlockingSchedulerPriority,1085        fn.bind(null, a, b, c, d),1086      );1087    } finally {1088      executionContext = prevExecutionContext;1089      if (executionContext === NoContext) {1090        // Flush the immediate callbacks that were scheduled during this batch1091        resetRenderTimer();1092        flushSyncCallbackQueue();1093      }1094    }1095  }1096}1097export function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {1098  const prevExecutionContext = executionContext;1099  executionContext &= ~BatchedContext;1100  executionContext |= LegacyUnbatchedContext;1101  try {1102    return fn(a);1103  } finally {1104    executionContext = prevExecutionContext;1105    if (executionContext === NoContext) {1106      // Flush the immediate callbacks that were scheduled during this batch1107      resetRenderTimer();1108      flushSyncCallbackQueue();1109    }1110  }1111}1112export function flushSync<A, R>(fn: A => R, a: A): R {1113  const prevExecutionContext = executionContext;1114  if ((prevExecutionContext & (RenderContext | CommitContext)) !== NoContext) {1115    if (__DEV__) {1116      console.error(1117        'flushSync was called from inside a lifecycle method. React cannot ' +1118          'flush when React is already rendering. Consider moving this call to ' +1119          'a scheduler task or micro task.',1120      );1121    }1122    return fn(a);1123  }1124  executionContext |= BatchedContext;1125  if (decoupleUpdatePriorityFromScheduler) {1126    const previousLanePriority = getCurrentUpdateLanePriority();1127    try {1128      setCurrentUpdateLanePriority(SyncLanePriority);1129      if (fn) {1130        return runWithPriority(ImmediateSchedulerPriority, fn.bind(null, a));1131      } else {1132        return (undefined: $FlowFixMe);1133      }1134    } finally {1135      setCurrentUpdateLanePriority(previousLanePriority);1136      executionContext = prevExecutionContext;1137      // Flush the immediate callbacks that were scheduled during this batch.1138      // Note that this will happen even if batchedUpdates is higher up1139      // the stack.1140      flushSyncCallbackQueue();1141    }1142  } else {1143    try {1144      if (fn) {1145        return runWithPriority(ImmediateSchedulerPriority, fn.bind(null, a));1146      } else {1147        return (undefined: $FlowFixMe);1148      }1149    } finally {1150      executionContext = prevExecutionContext;1151      // Flush the immediate callbacks that were scheduled during this batch.1152      // Note that this will happen even if batchedUpdates is higher up1153      // the stack.1154      flushSyncCallbackQueue();1155    }1156  }1157}1158export function flushControlled(fn: () => mixed): void {1159  const prevExecutionContext = executionContext;1160  executionContext |= BatchedContext;1161  if (decoupleUpdatePriorityFromScheduler) {1162    const previousLanePriority = getCurrentUpdateLanePriority();1163    try {1164      setCurrentUpdateLanePriority(SyncLanePriority);1165      runWithPriority(ImmediateSchedulerPriority, fn);1166    } finally {1167      setCurrentUpdateLanePriority(previousLanePriority);1168      executionContext = prevExecutionContext;1169      if (executionContext === NoContext) {1170        // Flush the immediate callbacks that were scheduled during this batch1171        resetRenderTimer();1172        flushSyncCallbackQueue();1173      }1174    }1175  } else {1176    try {1177      runWithPriority(ImmediateSchedulerPriority, fn);1178    } finally {1179      executionContext = prevExecutionContext;1180      if (executionContext === NoContext) {1181        // Flush the immediate callbacks that were scheduled during this batch1182        resetRenderTimer();1183        flushSyncCallbackQueue();1184      }1185    }1186  }1187}1188export function pushRenderLanes(fiber: Fiber, lanes: Lanes) {1189  pushToStack(subtreeRenderLanesCursor, subtreeRenderLanes, fiber);1190  subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);1191  workInProgressRootIncludedLanes = mergeLanes(1192    workInProgressRootIncludedLanes,1193    lanes,1194  );1195}1196export function popRenderLanes(fiber: Fiber) {1197  subtreeRenderLanes = subtreeRenderLanesCursor.current;1198  popFromStack(subtreeRenderLanesCursor, fiber);1199}1200function prepareFreshStack(root: FiberRoot, lanes: Lanes) {1201  root.finishedWork = null;1202  root.finishedLanes = NoLanes;1203  const timeoutHandle = root.timeoutHandle;1204  if (timeoutHandle !== noTimeout) {1205    // The root previous suspended and scheduled a timeout to commit a fallback1206    // state. Now that we have additional work, cancel the timeout.1207    root.timeoutHandle = noTimeout;1208    // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above1209    cancelTimeout(timeoutHandle);1210  }1211  if (workInProgress !== null) {1212    let interruptedWork = workInProgress.return;1213    while (interruptedWork !== null) {1214      unwindInterruptedWork(interruptedWork);1215      interruptedWork = interruptedWork.return;1216    }1217  }1218  workInProgressRoot = root;1219  workInProgress = createWorkInProgress(root.current, null);1220  workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;1221  workInProgressRootExitStatus = RootIncomplete;1222  workInProgressRootFatalError = null;1223  workInProgressRootSkippedLanes = NoLanes;1224  workInProgressRootUpdatedLanes = NoLanes;1225  workInProgressRootPingedLanes = NoLanes;1226  if (enableSchedulerTracing) {1227    spawnedWorkDuringRender = null;1228  }1229  if (__DEV__) {1230    ReactStrictModeWarnings.discardPendingWarnings();1231  }1232}1233function handleError(root, thrownValue): void {1234  do {1235    let erroredWork = workInProgress;1236    try {1237      // Reset module-level state that was set during the render phase.1238      resetContextDependencies();1239      resetHooksAfterThrow();1240      resetCurrentDebugFiberInDEV();1241      // TODO: I found and added this missing line while investigating a1242      // separate issue. Write a regression test using string refs.1243      ReactCurrentOwner.current = null;1244      if (erroredWork === null || erroredWork.return === null) {1245        // Expected to be working on a non-root fiber. This is a fatal error1246        // because there's no ancestor that can handle it; the root is1247        // supposed to capture all errors that weren't caught by an error1248        // boundary.1249        workInProgressRootExitStatus = RootFatalErrored;1250        workInProgressRootFatalError = thrownValue;1251        // Set `workInProgress` to null. This represents advancing to the next1252        // sibling, or the parent if there are no siblings. But since the root1253        // has no siblings nor a parent, we set it to null. Usually this is1254        // handled by `completeUnitOfWork` or `unwindWork`, but since we're1255        // intentionally not calling those, we need set it here.1256        // TODO: Consider calling `unwindWork` to pop the contexts.1257        workInProgress = null;1258        return;1259      }1260      if (enableProfilerTimer && erroredWork.mode & ProfileMode) {1261        // Record the time spent rendering before an error was thrown. This1262        // avoids inaccurate Profiler durations in the case of a1263        // suspended render.1264        stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);1265      }1266      throwException(1267        root,1268        erroredWork.return,1269        erroredWork,1270        thrownValue,1271        workInProgressRootRenderLanes,1272      );1273      completeUnitOfWork(erroredWork);1274    } catch (yetAnotherThrownValue) {1275      // Something in the return path also threw.1276      thrownValue = yetAnotherThrownValue;1277      if (workInProgress === erroredWork && erroredWork !== null) {1278        // If this boundary has already errored, then we had trouble processing1279        // the error. Bubble it to the next boundary.1280        erroredWork = erroredWork.return;1281        workInProgress = erroredWork;1282      } else {1283        erroredWork = workInProgress;1284      }1285      continue;1286    }1287    // Return to the normal work loop.1288    return;1289  } while (true);1290}1291function pushDispatcher() {1292  const prevDispatcher = ReactCurrentDispatcher.current;1293  ReactCurrentDispatcher.current = ContextOnlyDispatcher;1294  if (prevDispatcher === null) {1295    // The React isomorphic package does not include a default dispatcher.1296    // Instead the first renderer will lazily attach one, in order to give1297    // nicer error messages.1298    return ContextOnlyDispatcher;1299  } else {1300    return prevDispatcher;1301  }1302}1303function popDispatcher(prevDispatcher) {1304  ReactCurrentDispatcher.current = prevDispatcher;1305}1306function pushInteractions(root) {1307  if (enableSchedulerTracing) {1308    const prevInteractions: Set<Interaction> | null = __interactionsRef.current;1309    __interactionsRef.current = root.memoizedInteractions;1310    return prevInteractions;1311  }1312  return null;1313}1314function popInteractions(prevInteractions) {1315  if (enableSchedulerTracing) {1316    __interactionsRef.current = prevInteractions;1317  }1318}1319export function markCommitTimeOfFallback() {1320  globalMostRecentFallbackTime = now();1321}1322export function markSkippedUpdateLanes(lane: Lane | Lanes): void {1323  workInProgressRootSkippedLanes = mergeLanes(1324    lane,1325    workInProgressRootSkippedLanes,1326  );1327}1328export function renderDidSuspend(): void {1329  if (workInProgressRootExitStatus === RootIncomplete) {1330    workInProgressRootExitStatus = RootSuspended;1331  }1332}1333export function renderDidSuspendDelayIfPossible(): void {1334  if (1335    workInProgressRootExitStatus === RootIncomplete ||1336    workInProgressRootExitStatus === RootSuspended1337  ) {1338    workInProgressRootExitStatus = RootSuspendedWithDelay;1339  }1340  // Check if there are updates that we skipped tree that might have unblocked1341  // this render.1342  if (1343    workInProgressRoot !== null &&1344    (includesNonIdleWork(workInProgressRootSkippedLanes) ||1345      includesNonIdleWork(workInProgressRootUpdatedLanes))1346  ) {1347    // Mark the current render as suspended so that we switch to working on1348    // the updates that were skipped. Usually we only suspend at the end of1349    // the render phase.1350    // TODO: We should probably always mark the root as suspended immediately1351    // (inside this function), since by suspending at the end of the render1352    // phase introduces a potential mistake where we suspend lanes that were1353    // pinged or updated while we were rendering.1354    markRootSuspended(workInProgressRoot, workInProgressRootRenderLanes);1355  }1356}1357export function renderDidError() {1358  if (workInProgressRootExitStatus !== RootCompleted) {1359    workInProgressRootExitStatus = RootErrored;1360  }1361}1362// Called during render to determine if anything has suspended.1363// Returns false if we're not sure.1364export function renderHasNotSuspendedYet(): boolean {1365  // If something errored or completed, we can't really be sure,1366  // so those are false.1367  return workInProgressRootExitStatus === RootIncomplete;1368}1369function renderRootSync(root: FiberRoot, lanes: Lanes) {1370  const prevExecutionContext = executionContext;1371  executionContext |= RenderContext;1372  const prevDispatcher = pushDispatcher();1373  // If the root or lanes have changed, throw out the existing stack1374  // and prepare a fresh one. Otherwise we'll continue where we left off.1375  if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1376    prepareFreshStack(root, lanes);1377    startWorkOnPendingInteractions(root, lanes);1378  }1379  const prevInteractions = pushInteractions(root);1380  if (__DEV__) {1381    if (enableDebugTracing) {1382      logRenderStarted(lanes);1383    }1384  }1385  if (enableSchedulingProfiler) {1386    markRenderStarted(lanes);1387  }1388  do {1389    try {1390      workLoopSync();1391      break;1392    } catch (thrownValue) {1393      handleError(root, thrownValue);1394    }1395  } while (true);1396  resetContextDependencies();1397  if (enableSchedulerTracing) {1398    popInteractions(((prevInteractions: any): Set<Interaction>));1399  }1400  executionContext = prevExecutionContext;1401  popDispatcher(prevDispatcher);1402  if (workInProgress !== null) {1403    // This is a sync render, so we should have finished the whole tree.1404    invariant(1405      false,1406      'Cannot commit an incomplete root. This error is likely caused by a ' +1407        'bug in React. Please file an issue.',1408    );1409  }1410  if (__DEV__) {1411    if (enableDebugTracing) {1412      logRenderStopped();1413    }1414  }1415  if (enableSchedulingProfiler) {1416    markRenderStopped();1417  }1418  // Set this to null to indicate there's no in-progress render.1419  workInProgressRoot = null;1420  workInProgressRootRenderLanes = NoLanes;1421  return workInProgressRootExitStatus;1422}1423// The work loop is an extremely hot path. Tell Closure not to inline it.1424/** @noinline */1425function workLoopSync() {1426  // Already timed out, so perform work without checking if we need to yield.1427  while (workInProgress !== null) {1428    performUnitOfWork(workInProgress);1429  }1430}1431function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {1432  const prevExecutionContext = executionContext;1433  executionContext |= RenderContext;1434  const prevDispatcher = pushDispatcher();1435  // If the root or lanes have changed, throw out the existing stack1436  // and prepare a fresh one. Otherwise we'll continue where we left off.1437  if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1438    resetRenderTimer();1439    prepareFreshStack(root, lanes);1440    startWorkOnPendingInteractions(root, lanes);1441  }1442  const prevInteractions = pushInteractions(root);1443  if (__DEV__) {1444    if (enableDebugTracing) {1445      logRenderStarted(lanes);1446    }1447  }1448  if (enableSchedulingProfiler) {1449    markRenderStarted(lanes);1450  }1451  do {1452    try {1453      workLoopConcurrent();1454      break;1455    } catch (thrownValue) {1456      handleError(root, thrownValue);1457    }1458  } while (true);1459  resetContextDependencies();1460  if (enableSchedulerTracing) {1461    popInteractions(((prevInteractions: any): Set<Interaction>));1462  }1463  popDispatcher(prevDispatcher);1464  executionContext = prevExecutionContext;1465  if (__DEV__) {1466    if (enableDebugTracing) {1467      logRenderStopped();1468    }1469  }1470  // Check if the tree has completed.1471  if (workInProgress !== null) {1472    // Still work remaining.1473    if (enableSchedulingProfiler) {1474      markRenderYielded();1475    }1476    return RootIncomplete;1477  } else {1478    // Completed the tree.1479    if (enableSchedulingProfiler) {1480      markRenderStopped();1481    }1482    // Set this to null to indicate there's no in-progress render.1483    workInProgressRoot = null;1484    workInProgressRootRenderLanes = NoLanes;1485    // Return the final exit status.1486    return workInProgressRootExitStatus;1487  }1488}1489/** @noinline */1490function workLoopConcurrent() {1491  // Perform work until Scheduler asks us to yield1492  while (workInProgress !== null && !shouldYield()) {1493    performUnitOfWork(workInProgress);1494  }1495}1496function performUnitOfWork(unitOfWork: Fiber): void {1497  // The current, flushed, state of this fiber is the alternate. Ideally1498  // nothing should rely on this, but relying on it here means that we don't1499  // need an additional field on the work in progress.1500  const current = unitOfWork.alternate;1501  setCurrentDebugFiberInDEV(unitOfWork);1502  let next;1503  if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {1504    startProfilerTimer(unitOfWork);1505    next = beginWork(current, unitOfWork, subtreeRenderLanes);1506    stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);1507  } else {1508    next = beginWork(current, unitOfWork, subtreeRenderLanes);1509  }1510  resetCurrentDebugFiberInDEV();1511  unitOfWork.memoizedProps = unitOfWork.pendingProps;1512  if (next === null) {1513    // If this doesn't spawn new work, complete the current work.1514    completeUnitOfWork(unitOfWork);1515  } else {1516    workInProgress = next;1517  }1518  ReactCurrentOwner.current = null;1519}1520function completeUnitOfWork(unitOfWork: Fiber): void {1521  // Attempt to complete the current unit of work, then move to the next1522  // sibling. If there are no more siblings, return to the parent fiber.1523  let completedWork = unitOfWork;1524  do {1525    // The current, flushed, state of this fiber is the alternate. Ideally1526    // nothing should rely on this, but relying on it here means that we don't1527    // need an additional field on the work in progress.1528    const current = completedWork.alternate;1529    const returnFiber = completedWork.return;1530    // Check if the work completed or if something threw.1531    if ((completedWork.flags & Incomplete) === NoFlags) {1532      setCurrentDebugFiberInDEV(completedWork);1533      let next;1534      if (1535        !enableProfilerTimer ||1536        (completedWork.mode & ProfileMode) === NoMode1537      ) {1538        next = completeWork(current, completedWork, subtreeRenderLanes);1539      } else {1540        startProfilerTimer(completedWork);1541        next = completeWork(current, completedWork, subtreeRenderLanes);1542        // Update render duration assuming we didn't error.1543        stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1544      }1545      resetCurrentDebugFiberInDEV();1546      if (next !== null) {1547        // Completing this fiber spawned new work. Work on that next.1548        workInProgress = next;1549        return;1550      }1551    } else {1552      // This fiber did not complete because something threw. Pop values off1553      // the stack without entering the complete phase. If this is a boundary,1554      // capture values if possible.1555      const next = unwindWork(completedWork, subtreeRenderLanes);1556      // Because this fiber did not complete, don't reset its expiration time.1557      if (next !== null) {1558        // If completing this work spawned new work, do that next. We'll come1559        // back here again.1560        // Since we're restarting, remove anything that is not a host effect1561        // from the effect tag.1562        next.flags &= HostEffectMask;1563        workInProgress = next;1564        return;1565      }1566      if (1567        enableProfilerTimer &&1568        (completedWork.mode & ProfileMode) !== NoMode1569      ) {1570        // Record the render duration for the fiber that errored.1571        stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1572        // Include the time spent working on failed children before continuing.1573        let actualDuration = completedWork.actualDuration;1574        let child = completedWork.child;1575        while (child !== null) {1576          actualDuration += child.actualDuration;1577          child = child.sibling;1578        }1579        completedWork.actualDuration = actualDuration;1580      }1581      if (returnFiber !== null) {1582        // Mark the parent fiber as incomplete1583        returnFiber.flags |= Incomplete;1584        returnFiber.subtreeFlags = NoFlags;1585        returnFiber.deletions = null;1586      }1587    }1588    const siblingFiber = completedWork.sibling;1589    if (siblingFiber !== null) {1590      // If there is more work to do in this returnFiber, do that next.1591      workInProgress = siblingFiber;1592      return;1593    }1594    // Otherwise, return to the parent1595    completedWork = returnFiber;1596    // Update the next thing we're working on in case something throws.1597    workInProgress = completedWork;1598  } while (completedWork !== null);1599  // We've reached the root.1600  if (workInProgressRootExitStatus === RootIncomplete) {1601    workInProgressRootExitStatus = RootCompleted;1602  }1603}1604function commitRoot(root) {1605  const renderPriorityLevel = getCurrentPriorityLevel();1606  runWithPriority(1607    ImmediateSchedulerPriority,1608    commitRootImpl.bind(null, root, renderPriorityLevel),1609  );1610  return null;1611}1612function commitRootImpl(root, renderPriorityLevel) {1613  do {1614    // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which1615    // means `flushPassiveEffects` will sometimes result in additional1616    // passive effects. So we need to keep flushing in a loop until there are1617    // no more pending effects.1618    // TODO: Might be better if `flushPassiveEffects` did not automatically1619    // flush synchronous work at the end, to avoid factoring hazards like this.1620    flushPassiveEffects();1621  } while (rootWithPendingPassiveEffects !== null);1622  flushRenderPhaseStrictModeWarningsInDEV();1623  invariant(1624    (executionContext & (RenderContext | CommitContext)) === NoContext,1625    'Should not already be working.',1626  );1627  const finishedWork = root.finishedWork;1628  const lanes = root.finishedLanes;1629  if (__DEV__) {1630    if (enableDebugTracing) {1631      logCommitStarted(lanes);1632    }1633  }1634  if (enableSchedulingProfiler) {1635    markCommitStarted(lanes);1636  }1637  // 没æéè¦æ´æ°çFiberèç¹1638  if (finishedWork === null) {1639    if (__DEV__) {1640      if (enableDebugTracing) {1641        logCommitStopped();1642      }1643    }1644    if (enableSchedulingProfiler) {1645      markCommitStopped();1646    }1647    return null;1648  }1649  root.finishedWork = null;1650  root.finishedLanes = NoLanes;1651  invariant(1652    finishedWork !== root.current,1653    'Cannot commit the same tree as before. This error is likely caused by ' +1654      'a bug in React. Please file an issue.',1655  );1656  // commitRoot never returns a continuation; it always finishes synchronously.1657  // So we can clear these now to allow a new callback to be scheduled.1658  root.callbackNode = null;1659  // Update the first and last pending times on this root. The new first1660  // pending time is whatever is left on the root fiber.1661  // åå¹¶ææçéé1662  let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);1663  markRootFinished(root, remainingLanes);1664  // Clear already finished discrete updates in case that a later call of1665  // `flushDiscreteUpdates` starts a useless render pass which may cancels1666  // a scheduled timeout.1667  if (rootsWithPendingDiscreteUpdates !== null) {1668    if (1669      !hasDiscreteLanes(remainingLanes) &&1670      rootsWithPendingDiscreteUpdates.has(root)1671    ) {1672      rootsWithPendingDiscreteUpdates.delete(root);1673    }1674  }1675  // è¯´ææ¬æ¬¡æ´æ°å·²ç»å®æäºï¼å°ä¸äºåééç½®1676  if (root === workInProgressRoot) {1677    // We can reset these now that they are finished.1678    workInProgressRoot = null;1679    workInProgress = null;1680    workInProgressRootRenderLanes = NoLanes;1681  } else {1682    // This indicates that the last root we worked on is not the same one that1683    // we're committing now. This most commonly happens when a suspended root1684    // times out.1685  }1686  // Check if there are any effects in the whole tree.1687  // TODO: This is left over from the effect list implementation, where we had1688  // to check for the existence of `firstEffect` to satsify Flow. I think the1689  // only other reason this optimization exists is because it affects profiling.1690  // Reconsider whether this is necessary.1691  const subtreeHasEffects =1692    (finishedWork.subtreeFlags &1693      (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1694    NoFlags;1695  const rootHasEffect =1696    (finishedWork.flags &1697      (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1698    NoFlags;1699    // æéè¦å¤ççå¯ä½ç¨1700  if (subtreeHasEffects || rootHasEffect) {1701    let previousLanePriority;1702    if (decoupleUpdatePriorityFromScheduler) {1703      previousLanePriority = getCurrentUpdateLanePriority();1704      setCurrentUpdateLanePriority(SyncLanePriority);1705    }1706    // 设置æ§è¡ä¸ä¸æ1707    const prevExecutionContext = executionContext;1708    executionContext |= CommitContext;1709    const prevInteractions = pushInteractions(root);1710    // Reset this to null before calling lifecycles1711    ReactCurrentOwner.current = null;1712    // The commit phase is broken into several sub-phases. We do a separate pass1713    // of the effect list for each phase: all mutation effects come before all1714    // layout effects, and so on.1715    // The first phase a "before mutation" phase. We use this phase to read the1716    // state of the host tree right before we mutate it. This is where1717    // getSnapshotBeforeUpdate is called.1718    focusedInstanceHandle = prepareForCommit(root.containerInfo);1719    shouldFireAfterActiveInstanceBlur = false;1720    commitBeforeMutationEffects(finishedWork);1721    // We no longer need to track the active instance fiber1722    focusedInstanceHandle = null;1723    if (enableProfilerTimer) {1724      // Mark the current commit time to be shared by all Profilers in this1725      // batch. This enables them to be grouped later.1726      recordCommitTime();1727    }1728    // æDOMå
素渲æå°é¡µé¢ä¸1729    // The next phase is the mutation phase, where we mutate the host tree.1730    commitMutationEffects(finishedWork, root, renderPriorityLevel);1731    if (shouldFireAfterActiveInstanceBlur) {1732      afterActiveInstanceBlur();1733    }1734    resetAfterCommit(root.containerInfo);1735    // The work-in-progress tree is now the current tree. This must come after1736    // the mutation phase, so that the previous tree is still current during1737    // componentWillUnmount, but before the layout phase, so that the finished1738    // work is current during componentDidMount/Update.1739    // 卿¤ä¹åï¼root.current æåäºwork-in-progress tree1740    root.current = finishedWork;1741    // The next phase is the layout phase, where we call effects that read1742    // the host tree after it's been mutated. The idiomatic use case for this is1743    // layout, but class component lifecycles also fire here for legacy reasons.1744    if (__DEV__) {1745      if (enableDebugTracing) {1746        logLayoutEffectsStarted(lanes);1747      }1748    }1749    if (enableSchedulingProfiler) {1750      markLayoutEffectsStarted(lanes);1751    }1752    if (__DEV__) {1753      setCurrentDebugFiberInDEV(finishedWork);1754      invokeGuardedCallback(1755        null,1756        recursivelyCommitLayoutEffects,1757        null,1758        finishedWork,1759        root,1760      );1761      if (hasCaughtError()) {1762        const error = clearCaughtError();1763        captureCommitPhaseErrorOnRoot(finishedWork, finishedWork, error);1764      }1765      resetCurrentDebugFiberInDEV();1766    } else {1767      try {1768        recursivelyCommitLayoutEffects(finishedWork, root);1769      } catch (error) {1770        captureCommitPhaseErrorOnRoot(finishedWork, finishedWork, error);1771      }1772    }1773    if (__DEV__) {1774      if (enableDebugTracing) {1775        logLayoutEffectsStopped();1776      }1777    }1778    if (enableSchedulingProfiler) {1779      markLayoutEffectsStopped();1780    }1781    // If there are pending passive effects, schedule a callback to process them.1782    if (1783      (finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||1784      (finishedWork.flags & PassiveMask) !== NoFlags1785    ) {1786      if (!rootDoesHavePassiveEffects) {1787        rootDoesHavePassiveEffects = true;1788        scheduleCallback(NormalSchedulerPriority, () => {1789          flushPassiveEffects();1790          return null;1791        });1792      }1793    }1794    // Tell Scheduler to yield at the end of the frame, so the browser has an1795    // opportunity to paint.1796    requestPaint();1797    if (enableSchedulerTracing) {1798      popInteractions(((prevInteractions: any): Set<Interaction>));1799    }1800    executionContext = prevExecutionContext;1801    if (decoupleUpdatePriorityFromScheduler && previousLanePriority != null) {1802      // Reset the priority to the previous non-sync value.1803      setCurrentUpdateLanePriority(previousLanePriority);1804    }1805  } else {1806    // No effects.1807    root.current = finishedWork;1808    // Measure these anyway so the flamegraph explicitly shows that there were1809    // no effects.1810    // TODO: Maybe there's a better way to report this.1811    if (enableProfilerTimer) {1812      recordCommitTime();1813    }1814  }1815  const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;1816  if (rootDoesHavePassiveEffects) {1817    // This commit has passive effects. Stash a reference to them. But don't1818    // schedule a callback until after flushing layout work.1819    rootDoesHavePassiveEffects = false;1820    rootWithPendingPassiveEffects = root;1821    pendingPassiveEffectsLanes = lanes;1822    pendingPassiveEffectsRenderPriority = renderPriorityLevel;1823  }1824  // Read this again, since an effect might have updated it1825  remainingLanes = root.pendingLanes;1826  // Check if there's remaining work on this root1827  if (remainingLanes !== NoLanes) {1828    if (enableSchedulerTracing) {1829      if (spawnedWorkDuringRender !== null) {1830        const expirationTimes = spawnedWorkDuringRender;1831        spawnedWorkDuringRender = null;1832        for (let i = 0; i < expirationTimes.length; i++) {1833          scheduleInteractions(1834            root,1835            expirationTimes[i],1836            root.memoizedInteractions,1837          );1838        }1839      }1840      schedulePendingInteractions(root, remainingLanes);1841    }1842  } else {1843    // If there's no remaining work, we can clear the set of already failed1844    // error boundaries.1845    legacyErrorBoundariesThatAlreadyFailed = null;1846  }1847  if (__DEV__ && enableDoubleInvokingEffects) {1848    if (!rootDidHavePassiveEffects) {1849      commitDoubleInvokeEffectsInDEV(root.current, false);1850    }1851  }1852  if (enableSchedulerTracing) {1853    if (!rootDidHavePassiveEffects) {1854      // If there are no passive effects, then we can complete the pending interactions.1855      // Otherwise, we'll wait until after the passive effects are flushed.1856      // Wait to do this until after remaining work has been scheduled,1857      // so that we don't prematurely signal complete for interactions when there's e.g. hidden work.1858      finishPendingInteractions(root, lanes);1859    }1860  }1861  if (remainingLanes === SyncLane) {1862    // Count the number of times the root synchronously re-renders without1863    // finishing. If there are too many, it indicates an infinite update loop.1864    if (root === rootWithNestedUpdates) {1865      nestedUpdateCount++;1866    } else {1867      nestedUpdateCount = 0;1868      rootWithNestedUpdates = root;1869    }1870  } else {1871    nestedUpdateCount = 0;1872  }1873  onCommitRootDevTools(finishedWork.stateNode, renderPriorityLevel);1874  if (__DEV__) {1875    onCommitRootTestSelector();1876  }1877  // Always call this before exiting `commitRoot`, to ensure that any1878  // additional work on this root is scheduled.1879  ensureRootIsScheduled(root, now());1880  if (hasUncaughtError) {1881    hasUncaughtError = false;1882    const error = firstUncaughtError;1883    firstUncaughtError = null;1884    throw error;1885  }1886  if ((executionContext & LegacyUnbatchedContext) !== NoContext) {1887    if (__DEV__) {1888      if (enableDebugTracing) {1889        logCommitStopped();1890      }1891    }1892    if (enableSchedulingProfiler) {1893      markCommitStopped();1894    }1895    // This is a legacy edge case. We just committed the initial mount of1896    // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1897    // synchronously, but layout updates should be deferred until the end1898    // of the batch.1899    return null;1900  }1901  // If layout work was scheduled, flush it now.1902  flushSyncCallbackQueue();1903  if (__DEV__) {1904    if (enableDebugTracing) {1905      logCommitStopped();1906    }1907  }1908  if (enableSchedulingProfiler) {1909    markCommitStopped();1910  }1911  return null;1912}1913function commitBeforeMutationEffects(firstChild: Fiber) {1914  let fiber = firstChild;1915  while (fiber !== null) {1916    // å¤çéè¦å é¤çfiber autoFocusãblur é»è¾ã1917    if (fiber.deletions !== null) {1918      commitBeforeMutationEffectsDeletions(fiber.deletions);1919    }1920    // éå½è°ç¨å¤çåèç¹1921    if (fiber.child !== null) {1922      const primarySubtreeFlags = fiber.subtreeFlags & BeforeMutationMask;1923      if (primarySubtreeFlags !== NoFlags) {1924        commitBeforeMutationEffects(fiber.child);1925      }1926    }1927    if (__DEV__) {1928      setCurrentDebugFiberInDEV(fiber);1929      invokeGuardedCallback(null, commitBeforeMutationEffectsImpl, null, fiber);1930      if (hasCaughtError()) {1931        const error = clearCaughtError();1932        captureCommitPhaseError(fiber, fiber.return, error);1933      }1934      resetCurrentDebugFiberInDEV();1935    } else {1936      try {1937        // è°ç¨ getSnapshotBeforeUpdate çå½å¨æ1938        // 弿¥è°åº¦useEffect1939        commitBeforeMutationEffectsImpl(fiber);1940      } catch (error) {1941        captureCommitPhaseError(fiber, fiber.return, error);1942      }1943    }1944    // è¿åå
å¼èç¹ï¼æ¥ç循ç¯1945    fiber = fiber.sibling;1946  }1947}1948function commitBeforeMutationEffectsImpl(fiber: Fiber) {1949  const current = fiber.alternate;1950  const flags = fiber.flags;1951  if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {1952    // Check to see if the focused element was inside of a hidden (Suspense) subtree.1953    // TODO: Move this out of the hot path using a dedicated effect tag.1954    if (1955      fiber.tag === SuspenseComponent &&1956      isSuspenseBoundaryBeingHidden(current, fiber) &&1957      doesFiberContain(fiber, focusedInstanceHandle)1958    ) {1959      shouldFireAfterActiveInstanceBlur = true;1960      beforeActiveInstanceBlur();1961    }1962  }1963  if ((flags & Snapshot) !== NoFlags) {1964    setCurrentDebugFiberInDEV(fiber);1965    // è°ç¨ getSnapshotBeforeUpdate çå½å¨æ1966    commitBeforeMutationEffectOnFiber(current, fiber);1967    resetCurrentDebugFiberInDEV();1968  }1969  // è°åº¦useEffect1970  if ((flags & Passive) !== NoFlags) {1971    // If there are passive effects, schedule a callback to flush at1972    // the earliest opportunity.1973    if (!rootDoesHavePassiveEffects) {1974      rootDoesHavePassiveEffects = true;1975      scheduleCallback(NormalSchedulerPriority, () => {1976        flushPassiveEffects();1977        return null;1978      });1979    }1980  }1981}1982function commitBeforeMutationEffectsDeletions(deletions: Array<Fiber>) {1983  for (let i = 0; i < deletions.length; i++) {1984    const fiber = deletions[i];1985    // TODO (effects) It would be nice to avoid calling doesFiberContain()1986    // Maybe we can repurpose one of the subtreeFlags positions for this instead?1987    // Use it to store which part of the tree the focused instance is in?1988    // This assumes we can safely determine that instance during the "render" phase.1989    if (doesFiberContain(fiber, ((focusedInstanceHandle: any): Fiber))) {1990      shouldFireAfterActiveInstanceBlur = true;1991      beforeActiveInstanceBlur();1992    }1993  }1994}1995function commitMutationEffects(1996  firstChild: Fiber,1997  root: FiberRoot,1998  renderPriorityLevel: ReactPriorityLevel,1999) {2000  let fiber = firstChild;2001  while (fiber !== null) {2002    const deletions = fiber.deletions;2003    if (deletions !== null) {2004      commitMutationEffectsDeletions(2005        deletions,2006        fiber,2007        root,2008        renderPriorityLevel,2009      );2010    }2011    if (fiber.child !== null) {2012      const mutationFlags = fiber.subtreeFlags & MutationMask;2013      if (mutationFlags !== NoFlags) {2014        commitMutationEffects(fiber.child, root, renderPriorityLevel);2015      }2016    }2017    if (__DEV__) {2018      setCurrentDebugFiberInDEV(fiber);2019      invokeGuardedCallback(2020        null,2021        commitMutationEffectsImpl,2022        null,2023        fiber,2024        root,2025        renderPriorityLevel,2026      );2027      if (hasCaughtError()) {2028        const error = clearCaughtError();2029        captureCommitPhaseError(fiber, fiber.return, error);2030      }2031      resetCurrentDebugFiberInDEV();2032    } else {2033      try {2034        commitMutationEffectsImpl(fiber, root, renderPriorityLevel);2035      } catch (error) {2036        captureCommitPhaseError(fiber, fiber.return, error);2037      }2038    }2039    fiber = fiber.sibling;2040  }2041}2042function commitMutationEffectsImpl(2043  fiber: Fiber,2044  root: FiberRoot,2045  renderPriorityLevel,2046) {2047  const flags = fiber.flags;2048  if (flags & ContentReset) {2049    commitResetTextContent(fiber);2050  }2051  if (flags & Ref) {2052    const current = fiber.alternate;2053    if (current !== null) {2054      commitDetachRef(current);2055    }2056    if (enableScopeAPI) {2057      // TODO: This is a temporary solution that allowed us to transition away from React Flare on www.2058      if (fiber.tag === ScopeComponent) {2059        commitAttachRef(fiber);2060      }2061    }2062  }2063  // The following switch statement is only concerned about placement,2064  // updates, and deletions. To avoid needing to add a case for every possible2065  // bitmap value, we remove the secondary effects from the effect tag and2066  // switch on that value.2067  const primaryFlags = flags & (Placement | Update | Hydrating);2068  switch (primaryFlags) {2069    case Placement: {2070      commitPlacement(fiber);2071      // Clear the "placement" from effect tag so that we know that this is2072      // inserted, before any life-cycles like componentDidMount gets called.2073      // TODO: findDOMNode doesn't rely on this any more but isMounted does2074      // and isMounted is deprecated anyway so we should be able to kill this.2075      fiber.flags &= ~Placement;2076      break;2077    }2078    case PlacementAndUpdate: {2079      // Placement2080      commitPlacement(fiber);2081      // Clear the "placement" from effect tag so that we know that this is2082      // inserted, before any life-cycles like componentDidMount gets called.2083      fiber.flags &= ~Placement;2084      // Update2085      const current = fiber.alternate;2086      commitWork(current, fiber);2087      break;2088    }2089    case Hydrating: {2090      fiber.flags &= ~Hydrating;2091      break;2092    }2093    case HydratingAndUpdate: {2094      fiber.flags &= ~Hydrating;2095      // Update2096      const current = fiber.alternate;2097      commitWork(current, fiber);2098      break;2099    }2100    case Update: {2101      const current = fiber.alternate;2102      commitWork(current, fiber);2103      break;2104    }2105  }2106}2107function commitMutationEffectsDeletions(2108  deletions: Array<Fiber>,2109  nearestMountedAncestor: Fiber,2110  root: FiberRoot,2111  renderPriorityLevel,2112) {2113  for (let i = 0; i < deletions.length; i++) {2114    const childToDelete = deletions[i];2115    if (__DEV__) {2116      invokeGuardedCallback(2117        null,2118        commitDeletion,2119        null,2120        root,2121        childToDelete,2122        nearestMountedAncestor,2123        renderPriorityLevel,2124      );2125      if (hasCaughtError()) {2126        const error = clearCaughtError();2127        captureCommitPhaseError(childToDelete, nearestMountedAncestor, error);2128      }2129    } else {2130      try {2131        commitDeletion(2132          root,2133          childToDelete,2134          nearestMountedAncestor,2135          renderPriorityLevel,2136        );2137      } catch (error) {2138        captureCommitPhaseError(childToDelete, nearestMountedAncestor, error);2139      }2140    }2141  }2142}2143export function schedulePassiveEffectCallback() {2144  if (!rootDoesHavePassiveEffects) {2145    rootDoesHavePassiveEffects = true;2146    scheduleCallback(NormalSchedulerPriority, () => {2147      flushPassiveEffects();2148      return null;2149    });2150  }2151}2152export function flushPassiveEffects(): boolean {2153  // Returns whether passive effects were flushed.2154  if (pendingPassiveEffectsRenderPriority !== NoSchedulerPriority) {2155    const priorityLevel =2156      pendingPassiveEffectsRenderPriority > NormalSchedulerPriority2157        ? NormalSchedulerPriority2158        : pendingPassiveEffectsRenderPriority;2159    pendingPassiveEffectsRenderPriority = NoSchedulerPriority;2160    if (decoupleUpdatePriorityFromScheduler) {2161      const previousLanePriority = getCurrentUpdateLanePriority();2162      try {2163        setCurrentUpdateLanePriority(2164          schedulerPriorityToLanePriority(priorityLevel),2165        );2166        return runWithPriority(priorityLevel, flushPassiveEffectsImpl);2167      } finally {2168        setCurrentUpdateLanePriority(previousLanePriority);2169      }2170    } else {2171      return runWithPriority(priorityLevel, flushPassiveEffectsImpl);2172    }2173  }2174  return false;2175}2176function flushPassiveMountEffects(root, firstChild: Fiber): void {2177  let fiber = firstChild;2178  while (fiber !== null) {2179    let prevProfilerOnStack = null;2180    if (enableProfilerTimer && enableProfilerCommitHooks) {2181      if (fiber.tag === Profiler) {2182        prevProfilerOnStack = nearestProfilerOnStack;2183        nearestProfilerOnStack = fiber;2184      }2185    }2186    const primarySubtreeFlags = fiber.subtreeFlags & PassiveMask;2187    if (fiber.child !== null && primarySubtreeFlags !== NoFlags) {2188      flushPassiveMountEffects(root, fiber.child);2189    }2190    if ((fiber.flags & Passive) !== NoFlags) {2191      if (__DEV__) {2192        setCurrentDebugFiberInDEV(fiber);2193        invokeGuardedCallback(2194          null,2195          commitPassiveMountOnFiber,2196          null,2197          root,2198          fiber,2199        );2200        if (hasCaughtError()) {2201          const error = clearCaughtError();2202          captureCommitPhaseError(fiber, fiber.return, error);2203        }2204        resetCurrentDebugFiberInDEV();2205      } else {2206        try {2207          commitPassiveMountOnFiber(root, fiber);2208        } catch (error) {2209          captureCommitPhaseError(fiber, fiber.return, error);2210        }2211      }2212    }2213    if (enableProfilerTimer && enableProfilerCommitHooks) {2214      if (fiber.tag === Profiler) {2215        // Bubble times to the next nearest ancestor Profiler.2216        // After we process that Profiler, we'll bubble further up.2217        if (prevProfilerOnStack !== null) {2218          prevProfilerOnStack.stateNode.passiveEffectDuration +=2219            fiber.stateNode.passiveEffectDuration;2220        }2221        nearestProfilerOnStack = prevProfilerOnStack;2222      }2223    }2224    fiber = fiber.sibling;2225  }2226}2227function flushPassiveUnmountEffects(firstChild: Fiber): void {2228  let fiber = firstChild;2229  while (fiber !== null) {2230    const deletions = fiber.deletions;2231    if (deletions !== null) {2232      for (let i = 0; i < deletions.length; i++) {2233        const fiberToDelete = deletions[i];2234        flushPassiveUnmountEffectsInsideOfDeletedTree(fiberToDelete, fiber);2235        // Now that passive effects have been processed, it's safe to detach lingering pointers.2236        detachFiberAfterEffects(fiberToDelete);2237      }2238    }2239    const child = fiber.child;2240    if (child !== null) {2241      // If any children have passive effects then traverse the subtree.2242      // Note that this requires checking subtreeFlags of the current Fiber,2243      // rather than the subtreeFlags/effectsTag of the first child,2244      // since that would not cover passive effects in siblings.2245      const passiveFlags = fiber.subtreeFlags & PassiveMask;2246      if (passiveFlags !== NoFlags) {2247        flushPassiveUnmountEffects(child);2248      }2249    }2250    const primaryFlags = fiber.flags & Passive;2251    if (primaryFlags !== NoFlags) {2252      setCurrentDebugFiberInDEV(fiber);2253      commitPassiveUnmountOnFiber(fiber);2254      resetCurrentDebugFiberInDEV();2255    }2256    fiber = fiber.sibling;2257  }2258}2259function flushPassiveUnmountEffectsInsideOfDeletedTree(2260  fiberToDelete: Fiber,2261  nearestMountedAncestor: Fiber,2262): void {2263  if ((fiberToDelete.subtreeFlags & PassiveStatic) !== NoFlags) {2264    // If any children have passive effects then traverse the subtree.2265    // Note that this requires checking subtreeFlags of the current Fiber,2266    // rather than the subtreeFlags/effectsTag of the first child,2267    // since that would not cover passive effects in siblings.2268    let child = fiberToDelete.child;2269    while (child !== null) {2270      flushPassiveUnmountEffectsInsideOfDeletedTree(2271        child,2272        nearestMountedAncestor,2273      );2274      child = child.sibling;2275    }2276  }2277  if ((fiberToDelete.flags & PassiveStatic) !== NoFlags) {2278    setCurrentDebugFiberInDEV(fiberToDelete);2279    commitPassiveUnmountInsideDeletedTreeOnFiber(2280      fiberToDelete,2281      nearestMountedAncestor,2282    );2283    resetCurrentDebugFiberInDEV();2284  }2285}2286function flushPassiveEffectsImpl() {2287  if (rootWithPendingPassiveEffects === null) {2288    return false;2289  }2290  const root = rootWithPendingPassiveEffects;2291  const lanes = pendingPassiveEffectsLanes;2292  rootWithPendingPassiveEffects = null;2293  pendingPassiveEffectsLanes = NoLanes;2294  invariant(2295    (executionContext & (RenderContext | CommitContext)) === NoContext,2296    'Cannot flush passive effects while already rendering.',2297  );2298  if (__DEV__) {2299    if (enableDebugTracing) {2300      logPassiveEffectsStarted(lanes);2301    }2302  }2303  if (enableSchedulingProfiler) {2304    markPassiveEffectsStarted(lanes);2305  }2306  if (__DEV__) {2307    isFlushingPassiveEffects = true;2308  }2309  const prevExecutionContext = executionContext;2310  executionContext |= CommitContext;2311  const prevInteractions = pushInteractions(root);2312  // It's important that ALL pending passive effect destroy functions are called2313  // before ANY passive effect create functions are called.2314  // Otherwise effects in sibling components might interfere with each other.2315  // e.g. a destroy function in one component may unintentionally override a ref2316  // value set by a create function in another component.2317  // Layout effects have the same constraint.2318  flushPassiveUnmountEffects(root.current);2319  flushPassiveMountEffects(root, root.current);2320  if (__DEV__) {2321    if (enableDebugTracing) {2322      logPassiveEffectsStopped();2323    }2324  }2325  if (enableSchedulingProfiler) {2326    markPassiveEffectsStopped();2327  }2328  if (__DEV__ && enableDoubleInvokingEffects) {2329    commitDoubleInvokeEffectsInDEV(root.current, true);2330  }2331  if (__DEV__) {2332    isFlushingPassiveEffects = false;2333  }2334  if (enableSchedulerTracing) {2335    popInteractions(((prevInteractions: any): Set<Interaction>));2336    finishPendingInteractions(root, lanes);2337  }2338  executionContext = prevExecutionContext;2339  flushSyncCallbackQueue();2340  // If additional passive effects were scheduled, increment a counter. If this2341  // exceeds the limit, we'll fire a warning.2342  nestedPassiveUpdateCount =2343    rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;2344  return true;2345}2346export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {2347  return (2348    legacyErrorBoundariesThatAlreadyFailed !== null &&2349    legacyErrorBoundariesThatAlreadyFailed.has(instance)2350  );2351}2352export function markLegacyErrorBoundaryAsFailed(instance: mixed) {2353  if (legacyErrorBoundariesThatAlreadyFailed === null) {2354    legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);2355  } else {2356    legacyErrorBoundariesThatAlreadyFailed.add(instance);2357  }2358}2359function prepareToThrowUncaughtError(error: mixed) {2360  if (!hasUncaughtError) {2361    hasUncaughtError = true;2362    firstUncaughtError = error;2363  }2364}2365export const onUncaughtError = prepareToThrowUncaughtError;2366function captureCommitPhaseErrorOnRoot(2367  rootFiber: Fiber,2368  sourceFiber: Fiber,2369  error: mixed,2370) {2371  const errorInfo = createCapturedValue(error, sourceFiber);2372  const update = createRootErrorUpdate(rootFiber, errorInfo, (SyncLane: Lane));2373  enqueueUpdate(rootFiber, update);2374  const eventTime = requestEventTime();2375  const root = markUpdateLaneFromFiberToRoot(rootFiber, (SyncLane: Lane));2376  if (root !== null) {2377    markRootUpdated(root, SyncLane, eventTime);2378    ensureRootIsScheduled(root, eventTime);2379    schedulePendingInteractions(root, SyncLane);2380  }2381}2382export function captureCommitPhaseError(2383  sourceFiber: Fiber,2384  nearestMountedAncestor: Fiber | null,2385  error: mixed,2386) {2387  if (sourceFiber.tag === HostRoot) {2388    // Error was thrown at the root. There is no parent, so the root2389    // itself should capture it.2390    captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);2391    return;2392  }2393  let fiber = null;2394  if (skipUnmountedBoundaries) {2395    fiber = nearestMountedAncestor;2396  } else {2397    fiber = sourceFiber.return;2398  }2399  while (fiber !== null) {2400    if (fiber.tag === HostRoot) {2401      captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);2402      return;2403    } else if (fiber.tag === ClassComponent) {2404      const ctor = fiber.type;2405      const instance = fiber.stateNode;2406      if (2407        typeof ctor.getDerivedStateFromError === 'function' ||2408        (typeof instance.componentDidCatch === 'function' &&2409          !isAlreadyFailedLegacyErrorBoundary(instance))2410      ) {2411        const errorInfo = createCapturedValue(error, sourceFiber);2412        const update = createClassErrorUpdate(2413          fiber,2414          errorInfo,2415          (SyncLane: Lane),2416        );2417        enqueueUpdate(fiber, update);2418        const eventTime = requestEventTime();2419        const root = markUpdateLaneFromFiberToRoot(fiber, (SyncLane: Lane));2420        if (root !== null) {2421          markRootUpdated(root, SyncLane, eventTime);2422          ensureRootIsScheduled(root, eventTime);2423          schedulePendingInteractions(root, SyncLane);2424        }2425        return;2426      }2427    }2428    fiber = fiber.return;2429  }2430}2431export function pingSuspendedRoot(2432  root: FiberRoot,2433  wakeable: Wakeable,2434  pingedLanes: Lanes,2435) {2436  const pingCache = root.pingCache;2437  if (pingCache !== null) {2438    // The wakeable resolved, so we no longer need to memoize, because it will2439    // never be thrown again.2440    pingCache.delete(wakeable);2441  }2442  const eventTime = requestEventTime();2443  markRootPinged(root, pingedLanes, eventTime);2444  if (2445    workInProgressRoot === root &&2446    isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)2447  ) {2448    // Received a ping at the same priority level at which we're currently2449    // rendering. We might want to restart this render. This should mirror2450    // the logic of whether or not a root suspends once it completes.2451    // TODO: If we're rendering sync either due to Sync, Batched or expired,2452    // we should probably never restart.2453    // If we're suspended with delay, or if it's a retry, we'll always suspend2454    // so we can always restart.2455    if (2456      workInProgressRootExitStatus === RootSuspendedWithDelay ||2457      (workInProgressRootExitStatus === RootSuspended &&2458        includesOnlyRetries(workInProgressRootRenderLanes) &&2459        now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)2460    ) {2461      // Restart from the root.2462      prepareFreshStack(root, NoLanes);2463    } else {2464      // Even though we can't restart right now, we might get an2465      // opportunity later. So we mark this render as having a ping.2466      workInProgressRootPingedLanes = mergeLanes(2467        workInProgressRootPingedLanes,2468        pingedLanes,2469      );2470    }2471  }2472  ensureRootIsScheduled(root, eventTime);2473  schedulePendingInteractions(root, pingedLanes);2474}2475function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {2476  // The boundary fiber (a Suspense component or SuspenseList component)2477  // previously was rendered in its fallback state. One of the promises that2478  // suspended it has resolved, which means at least part of the tree was2479  // likely unblocked. Try rendering again, at a new expiration time.2480  if (retryLane === NoLane) {2481    retryLane = requestRetryLane(boundaryFiber);2482  }2483  // TODO: Special case idle priority?2484  const eventTime = requestEventTime();2485  const root = markUpdateLaneFromFiberToRoot(boundaryFiber, retryLane);2486  if (root !== null) {2487    markRootUpdated(root, retryLane, eventTime);2488    ensureRootIsScheduled(root, eventTime);2489    schedulePendingInteractions(root, retryLane);2490  }2491}2492export function retryDehydratedSuspenseBoundary(boundaryFiber: Fiber) {2493  const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2494  let retryLane = NoLane;2495  if (suspenseState !== null) {2496    retryLane = suspenseState.retryLane;2497  }2498  retryTimedOutBoundary(boundaryFiber, retryLane);2499}2500export function resolveRetryWakeable(boundaryFiber: Fiber, wakeable: Wakeable) {2501  let retryLane = NoLane; // Default2502  let retryCache: WeakSet<Wakeable> | Set<Wakeable> | null;2503  if (enableSuspenseServerRenderer) {2504    switch (boundaryFiber.tag) {2505      case SuspenseComponent:2506        retryCache = boundaryFiber.stateNode;2507        const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2508        if (suspenseState !== null) {2509          retryLane = suspenseState.retryLane;2510        }2511        break;2512      case SuspenseListComponent:2513        retryCache = boundaryFiber.stateNode;2514        break;2515      default:2516        invariant(2517          false,2518          'Pinged unknown suspense boundary type. ' +2519            'This is probably a bug in React.',2520        );2521    }2522  } else {2523    retryCache = boundaryFiber.stateNode;2524  }2525  if (retryCache !== null) {2526    // The wakeable resolved, so we no longer need to memoize, because it will2527    // never be thrown again.2528    retryCache.delete(wakeable);2529  }2530  retryTimedOutBoundary(boundaryFiber, retryLane);2531}2532// Computes the next Just Noticeable Difference (JND) boundary.2533// The theory is that a person can't tell the difference between small differences in time.2534// Therefore, if we wait a bit longer than necessary that won't translate to a noticeable2535// difference in the experience. However, waiting for longer might mean that we can avoid2536// showing an intermediate loading state. The longer we have already waited, the harder it2537// is to tell small differences in time. Therefore, the longer we've already waited,2538// the longer we can wait additionally. At some point we have to give up though.2539// We pick a train model where the next boundary commits at a consistent schedule.2540// These particular numbers are vague estimates. We expect to adjust them based on research.2541function jnd(timeElapsed: number) {2542  return timeElapsed < 1202543    ? 1202544    : timeElapsed < 4802545    ? 4802546    : timeElapsed < 10802547    ? 10802548    : timeElapsed < 19202549    ? 19202550    : timeElapsed < 30002551    ? 30002552    : timeElapsed < 43202553    ? 43202554    : ceil(timeElapsed / 1960) * 1960;2555}2556function checkForNestedUpdates() {2557  if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {2558    nestedUpdateCount = 0;2559    rootWithNestedUpdates = null;2560    invariant(2561      false,2562      'Maximum update depth exceeded. This can happen when a component ' +2563        'repeatedly calls setState inside componentWillUpdate or ' +2564        'componentDidUpdate. React limits the number of nested updates to ' +2565        'prevent infinite loops.',2566    );2567  }2568  if (__DEV__) {2569    if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {2570      nestedPassiveUpdateCount = 0;2571      console.error(2572        'Maximum update depth exceeded. This can happen when a component ' +2573          "calls setState inside useEffect, but useEffect either doesn't " +2574          'have a dependency array, or one of the dependencies changes on ' +2575          'every render.',2576      );2577    }2578  }2579}2580function flushRenderPhaseStrictModeWarningsInDEV() {2581  if (__DEV__) {2582    ReactStrictModeWarnings.flushLegacyContextWarning();2583    if (warnAboutDeprecatedLifecycles) {2584      ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();2585    }2586  }2587}2588function commitDoubleInvokeEffectsInDEV(2589  fiber: Fiber,2590  hasPassiveEffects: boolean,2591) {2592  if (__DEV__ && enableDoubleInvokingEffects) {2593    setCurrentDebugFiberInDEV(fiber);2594    invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectUnmountInDEV);2595    if (hasPassiveEffects) {2596      invokeEffectsInDev(2597        fiber,2598        MountPassiveDev,2599        invokePassiveEffectUnmountInDEV,2600      );2601    }2602    invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectMountInDEV);2603    if (hasPassiveEffects) {2604      invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectMountInDEV);2605    }2606    resetCurrentDebugFiberInDEV();2607  }2608}2609function invokeEffectsInDev(2610  firstChild: Fiber,2611  fiberFlags: Flags,2612  invokeEffectFn: (fiber: Fiber) => void,2613): void {2614  if (__DEV__ && enableDoubleInvokingEffects) {2615    let fiber = firstChild;2616    while (fiber !== null) {2617      if (fiber.child !== null) {2618        const primarySubtreeFlag = fiber.subtreeFlags & fiberFlags;2619        if (primarySubtreeFlag !== NoFlags) {2620          invokeEffectsInDev(fiber.child, fiberFlags, invokeEffectFn);2621        }2622      }2623      if ((fiber.flags & fiberFlags) !== NoFlags) {2624        invokeEffectFn(fiber);2625      }2626      fiber = fiber.sibling;2627    }2628  }2629}2630let didWarnStateUpdateForNotYetMountedComponent: Set<string> | null = null;2631function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber) {2632  if (__DEV__) {2633    if ((executionContext & RenderContext) !== NoContext) {2634      // We let the other warning about render phase updates deal with this one.2635      return;2636    }2637    if (!(fiber.mode & (BlockingMode | ConcurrentMode))) {2638      return;2639    }2640    const tag = fiber.tag;2641    if (2642      tag !== IndeterminateComponent &&2643      tag !== HostRoot &&2644      tag !== ClassComponent &&2645      tag !== FunctionComponent &&2646      tag !== ForwardRef &&2647      tag !== MemoComponent &&2648      tag !== SimpleMemoComponent &&2649      tag !== Block2650    ) {2651      // Only warn for user-defined components, not internal ones like Suspense.2652      return;2653    }2654    // We show the whole stack but dedupe on the top component's name because2655    // the problematic code almost always lies inside that component.2656    const componentName = getComponentName(fiber.type) || 'ReactComponent';2657    if (didWarnStateUpdateForNotYetMountedComponent !== null) {2658      if (didWarnStateUpdateForNotYetMountedComponent.has(componentName)) {2659        return;2660      }2661      didWarnStateUpdateForNotYetMountedComponent.add(componentName);2662    } else {2663      didWarnStateUpdateForNotYetMountedComponent = new Set([componentName]);2664    }2665    const previousFiber = ReactCurrentFiberCurrent;2666    try {2667      setCurrentDebugFiberInDEV(fiber);2668      console.error(2669        "Can't perform a React state update on a component that hasn't mounted yet. " +2670          'This indicates that you have a side-effect in your render function that ' +2671          'asynchronously later calls tries to update the component. Move this work to ' +2672          'useEffect instead.',2673      );2674    } finally {2675      if (previousFiber) {2676        setCurrentDebugFiberInDEV(fiber);2677      } else {2678        resetCurrentDebugFiberInDEV();2679      }2680    }2681  }2682}2683let didWarnStateUpdateForUnmountedComponent: Set<string> | null = null;2684function warnAboutUpdateOnUnmountedFiberInDEV(fiber) {2685  if (__DEV__) {2686    const tag = fiber.tag;2687    if (2688      tag !== HostRoot &&2689      tag !== ClassComponent &&2690      tag !== FunctionComponent &&2691      tag !== ForwardRef &&2692      tag !== MemoComponent &&2693      tag !== SimpleMemoComponent &&2694      tag !== Block2695    ) {2696      // Only warn for user-defined components, not internal ones like Suspense.2697      return;2698    }...ReactFiberWorkLoop.old.js
Source:ReactFiberWorkLoop.old.js  
...182    checkForNestedUpdates();183    warnAboutRenderPhaseUpdatesInDEV(fiber);184    var root = markUpdateLaneFromFiberToRoot(fiber, lane);185    if (root === null) {186      warnAboutUpdateOnUnmountedFiberInDEV(fiber);187      return null;188    } // Mark that the root has a pending update.189    markRootUpdated(root, lane, eventTime);190    if (root === workInProgressRoot) {191      // Received an update to a tree that's in the middle of rendering. Mark192      // that there was an interleaved update work on this root. Unless the193      // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render194      // phase update. In that case, we don't treat render phase updates as if195      // they were interleaved, for backwards compat reasons.196      {197        workInProgressRootUpdatedLanes = mergeLanes(workInProgressRootUpdatedLanes, lane);198      }199      if (workInProgressRootExitStatus === RootSuspendedWithDelay) {200        // The root already suspended with a delay, which means this render201        // definitely won't finish. Since we have a new update, let's mark it as202        // suspended now, right before marking the incoming update. This has the203        // effect of interrupting the current render and switching to the update.204        // TODO: Make sure this doesn't override pings that happen while we've205        // already started rendering.206        markRootSuspended$1(root, workInProgressRootRenderLanes);207      }208    } // TODO: requestUpdateLanePriority also reads the priority. Pass the209    // priority as an argument to that function and this one.210    var priorityLevel = getCurrentPriorityLevel();211    if (lane === SyncLane) {212      if ( // Check if we're inside unbatchedUpdates213      (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering214      (executionContext & (RenderContext | CommitContext)) === NoContext) {215        // Register pending interactions on the root to avoid losing traced interaction data.216        schedulePendingInteractions(root, lane); // This is a legacy edge case. The initial mount of a ReactDOM.render-ed217        // root inside of batchedUpdates should be synchronous, but layout updates218        // should be deferred until the end of the batch.219        performSyncWorkOnRoot(root);220      } else {221        ensureRootIsScheduled(root, eventTime);222        schedulePendingInteractions(root, lane);223        if (executionContext === NoContext) {224          // Flush the synchronous work now, unless we're already working or inside225          // a batch. This is intentionally inside scheduleUpdateOnFiber instead of226          // scheduleCallbackForFiber to preserve the ability to schedule a callback227          // without immediately flushing it. We only do this for user-initiated228          // updates, to preserve historical behavior of legacy mode.229          resetRenderTimer();230          flushSyncCallbackQueue();231        }232      }233    } else {234      // Schedule a discrete update but only if it's not Sync.235      if ((executionContext & DiscreteEventContext) !== NoContext && ( // Only updates at user-blocking priority or greater are considered236      // discrete, even inside a discrete event.237      priorityLevel === UserBlockingPriority$2 || priorityLevel === ImmediatePriority$1)) {238        // This is the result of a discrete event. Track the lowest priority239        // discrete update per root so we can flush them early, if needed.240        if (rootsWithPendingDiscreteUpdates === null) {241          rootsWithPendingDiscreteUpdates = new Set([root]);242        } else {243          rootsWithPendingDiscreteUpdates.add(root);244        }245      } // Schedule other updates after in case the callback is sync.246      ensureRootIsScheduled(root, eventTime);247      schedulePendingInteractions(root, lane);248    } // We use this when assigning a lane for a transition inside249    // `requestUpdateLane`. We assume it's the same as the root being updated,250    // since in the common case of a single root app it probably is. If it's not251    // the same root, then it's not a huge deal, we just might batch more stuff252    // together more than necessary.253    mostRecentlyUpdatedRoot = root;254  } // This is split into a separate function so we can mark a fiber with pending255  // work without treating it as a typical update that originates from an event;256  // e.g. retrying a Suspense boundary isn't an update, but it does schedule work257  // on a fiber.258  function markUpdateLaneFromFiberToRoot(sourceFiber, lane) {259    // Update the source fiber's lanes260    sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);261    var alternate = sourceFiber.alternate;262    if (alternate !== null) {263      alternate.lanes = mergeLanes(alternate.lanes, lane);264    }265    {266      if (alternate === null && (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags) {267        warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);268      }269    } // Walk the parent path to the root and update the child expiration time.270    var node = sourceFiber;271    var parent = sourceFiber.return;272    while (parent !== null) {273      parent.childLanes = mergeLanes(parent.childLanes, lane);274      alternate = parent.alternate;275      if (alternate !== null) {276        alternate.childLanes = mergeLanes(alternate.childLanes, lane);277      } else {278        {279          if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {280            warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);281          }282        }283      }284      node = parent;285      parent = parent.return;286    }287    if (node.tag === HostRoot) {288      var root = node.stateNode;289      return root;290    } else {291      return null;292    }293  } // Use this function to schedule a task for a root. There's only one task per294  // root; if a task was already scheduled, we'll check to make sure the priority295  // of the existing task is the same as the priority of the next level that the296  // root has work on. This function is called on every update, and right before297  // exiting a task.298  function ensureRootIsScheduled(root, currentTime) {299    var existingCallbackNode = root.callbackNode; // Check if any lanes are being starved by other work. If so, mark them as300    // expired so we know to work on those next.301    markStarvedLanesAsExpired(root, currentTime); // Determine the next lanes to work on, and their priority.302    var nextLanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes); // This returns the priority level computed during the `getNextLanes` call.303    var newCallbackPriority = returnNextLanesPriority();304    if (nextLanes === NoLanes) {305      // Special case: There's nothing to work on.306      if (existingCallbackNode !== null) {307        cancelCallback(existingCallbackNode);308        root.callbackNode = null;309        root.callbackPriority = NoLanePriority;310      }311      return;312    } // Check if there's an existing task. We may be able to reuse it.313    if (existingCallbackNode !== null) {314      var existingCallbackPriority = root.callbackPriority;315      if (existingCallbackPriority === newCallbackPriority) {316        // The priority hasn't changed. We can reuse the existing task. Exit.317        return;318      } // The priority changed. Cancel the existing callback. We'll schedule a new319      // one below.320      cancelCallback(existingCallbackNode);321    } // Schedule a new callback.322    var newCallbackNode;323    if (newCallbackPriority === SyncLanePriority) {324      // Special case: Sync React callbacks are scheduled on a special325      // internal queue326      newCallbackNode = scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));327    } else if (newCallbackPriority === SyncBatchedLanePriority) {328      newCallbackNode = scheduleCallback(ImmediatePriority$1, performSyncWorkOnRoot.bind(null, root));329    } else {330      var schedulerPriorityLevel = lanePriorityToSchedulerPriority(newCallbackPriority);331      newCallbackNode = scheduleCallback(schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root));332    }333    root.callbackPriority = newCallbackPriority;334    root.callbackNode = newCallbackNode;335  } // This is the entry point for every concurrent task, i.e. anything that336  // goes through Scheduler.337  function performConcurrentWorkOnRoot(root) {338    // Since we know we're in a React event, we can clear the current339    // event time. The next update will compute a new event time.340    currentEventTime = NoTimestamp;341    currentEventWipLanes = NoLanes;342    currentEventPendingLanes = NoLanes;343    if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {344      {345        throw Error( "Should not already be working." );346      }347    } // Flush any pending passive effects before deciding which lanes to work on,348    // in case they schedule additional work.349    var originalCallbackNode = root.callbackNode;350    var didFlushPassiveEffects = flushPassiveEffects();351    if (didFlushPassiveEffects) {352      // Something in the passive effect phase may have canceled the current task.353      // Check if the task node for this root was changed.354      if (root.callbackNode !== originalCallbackNode) {355        // The current task was canceled. Exit. We don't need to call356        // `ensureRootIsScheduled` because the check above implies either that357        // there's a new task, or that there's no remaining work on this root.358        return null;359      }360    } // Determine the next expiration time to work on, using the fields stored361    // on the root.362    var lanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes);363    if (lanes === NoLanes) {364      // Defensive coding. This is never expected to happen.365      return null;366    }367    var exitStatus = renderRootConcurrent(root, lanes);368    if (includesSomeLane(workInProgressRootIncludedLanes, workInProgressRootUpdatedLanes)) {369      // The render included lanes that were updated during the render phase.370      // For example, when unhiding a hidden tree, we include all the lanes371      // that were previously skipped when the tree was hidden. That set of372      // lanes is a superset of the lanes we started rendering with.373      //374      // So we'll throw out the current work and restart.375      prepareFreshStack(root, NoLanes);376    } else if (exitStatus !== RootIncomplete) {377      if (exitStatus === RootErrored) {378        executionContext |= RetryAfterError; // If an error occurred during hydration,379        // discard server response and fall back to client side render.380        if (root.hydrate) {381          root.hydrate = false;382          clearContainer(root.containerInfo);383        } // If something threw an error, try rendering one more time. We'll render384        // synchronously to block concurrent data mutations, and we'll includes385        // all pending updates are included. If it still fails after the second386        // attempt, we'll give up and commit the resulting tree.387        lanes = getLanesToRetrySynchronouslyOnError(root);388        if (lanes !== NoLanes) {389          exitStatus = renderRootSync(root, lanes);390        }391      }392      if (exitStatus === RootFatalErrored) {393        var fatalError = workInProgressRootFatalError;394        prepareFreshStack(root, NoLanes);395        markRootSuspended$1(root, lanes);396        ensureRootIsScheduled(root, now());397        throw fatalError;398      } // We now have a consistent tree. The next step is either to commit it,399      // or, if something suspended, wait to commit it after a timeout.400      var finishedWork = root.current.alternate;401      root.finishedWork = finishedWork;402      root.finishedLanes = lanes;403      finishConcurrentRender(root, exitStatus, lanes);404    }405    ensureRootIsScheduled(root, now());406    if (root.callbackNode === originalCallbackNode) {407      // The task node scheduled for this root is the same one that's408      // currently executed. Need to return a continuation.409      return performConcurrentWorkOnRoot.bind(null, root);410    }411    return null;412  }413  function finishConcurrentRender(root, exitStatus, lanes) {414    switch (exitStatus) {415      case RootIncomplete:416      case RootFatalErrored:417        {418          {419            {420              throw Error( "Root did not complete. This is a bug in React." );421            }422          }423        }424      // Flow knows about invariant, so it complains if I add a break425      // statement, but eslint doesn't know about invariant, so it complains426      // if I do. eslint-disable-next-line no-fallthrough427      case RootErrored:428        {429          // We should have already attempted to retry this tree. If we reached430          // this point, it errored again. Commit it.431          commitRoot(root);432          break;433        }434      case RootSuspended:435        {436          markRootSuspended$1(root, lanes); // We have an acceptable loading state. We need to figure out if we437          // should immediately commit it or wait a bit.438          if (includesOnlyRetries(lanes) && // do not delay if we're inside an act() scope439          !shouldForceFlushFallbacksInDEV()) {440            // This render only included retries, no updates. Throttle committing441            // retries so that we don't show too many loading states too quickly.442            var msUntilTimeout = globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now(); // Don't bother with a very short suspense time.443            if (msUntilTimeout > 10) {444              var nextLanes = getNextLanes(root, NoLanes);445              if (nextLanes !== NoLanes) {446                // There's additional work on this root.447                break;448              }449              var suspendedLanes = root.suspendedLanes;450              if (!isSubsetOfLanes(suspendedLanes, lanes)) {451                // We should prefer to render the fallback of at the last452                // suspended level. Ping the last suspended level to try453                // rendering it again.454                // FIXME: What if the suspended lanes are Idle? Should not restart.455                var eventTime = requestEventTime();456                markRootPinged(root, suspendedLanes);457                break;458              } // The render is suspended, it hasn't timed out, and there's no459              // lower priority work to do. Instead of committing the fallback460              // immediately, wait for more data to arrive.461              root.timeoutHandle = scheduleTimeout(commitRoot.bind(null, root), msUntilTimeout);462              break;463            }464          } // The work expired. Commit immediately.465          commitRoot(root);466          break;467        }468      case RootSuspendedWithDelay:469        {470          markRootSuspended$1(root, lanes);471          if (includesOnlyTransitions(lanes)) {472            // This is a transition, so we should exit without committing a473            // placeholder and without scheduling a timeout. Delay indefinitely474            // until we receive more data.475            break;476          }477          if (!shouldForceFlushFallbacksInDEV()) {478            // This is not a transition, but we did trigger an avoided state.479            // Schedule a placeholder to display after a short delay, using the Just480            // Noticeable Difference.481            // TODO: Is the JND optimization worth the added complexity? If this is482            // the only reason we track the event time, then probably not.483            // Consider removing.484            var mostRecentEventTime = getMostRecentEventTime(root, lanes);485            var eventTimeMs = mostRecentEventTime;486            var timeElapsedMs = now() - eventTimeMs;487            var _msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs; // Don't bother with a very short suspense time.488            if (_msUntilTimeout > 10) {489              // Instead of committing the fallback immediately, wait for more data490              // to arrive.491              root.timeoutHandle = scheduleTimeout(commitRoot.bind(null, root), _msUntilTimeout);492              break;493            }494          } // Commit the placeholder.495          commitRoot(root);496          break;497        }498      case RootCompleted:499        {500          // The work completed. Ready to commit.501          commitRoot(root);502          break;503        }504      default:505        {506          {507            {508              throw Error( "Unknown root exit status." );509            }510          }511        }512    }513  }514  function markRootSuspended$1(root, suspendedLanes) {515    // When suspending, we should always exclude lanes that were pinged or (more516    // rarely, since we try to avoid it) updated during the render phase.517    // TODO: Lol maybe there's a better way to factor this besides this518    // obnoxiously named function :)519    suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);520    suspendedLanes = removeLanes(suspendedLanes, workInProgressRootUpdatedLanes);521    markRootSuspended(root, suspendedLanes);522  } // This is the entry point for synchronous tasks that don't go523  // through Scheduler524  function performSyncWorkOnRoot(root) {525    if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {526      {527        throw Error( "Should not already be working." );528      }529    }530    flushPassiveEffects();531    var lanes;532    var exitStatus;533    if (root === workInProgressRoot && includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes)) {534      // There's a partial tree, and at least one of its lanes has expired. Finish535      // rendering it before rendering the rest of the expired work.536      lanes = workInProgressRootRenderLanes;537      exitStatus = renderRootSync(root, lanes);538      if (includesSomeLane(workInProgressRootIncludedLanes, workInProgressRootUpdatedLanes)) {539        // The render included lanes that were updated during the render phase.540        // For example, when unhiding a hidden tree, we include all the lanes541        // that were previously skipped when the tree was hidden. That set of542        // lanes is a superset of the lanes we started rendering with.543        //544        // Note that this only happens when part of the tree is rendered545        // concurrently. If the whole tree is rendered synchronously, then there546        // are no interleaved events.547        lanes = getNextLanes(root, lanes);548        exitStatus = renderRootSync(root, lanes);549      }550    } else {551      lanes = getNextLanes(root, NoLanes);552      exitStatus = renderRootSync(root, lanes);553    }554    if (root.tag !== LegacyRoot && exitStatus === RootErrored) {555      executionContext |= RetryAfterError; // If an error occurred during hydration,556      // discard server response and fall back to client side render.557      if (root.hydrate) {558        root.hydrate = false;559        clearContainer(root.containerInfo);560      } // If something threw an error, try rendering one more time. We'll render561      // synchronously to block concurrent data mutations, and we'll includes562      // all pending updates are included. If it still fails after the second563      // attempt, we'll give up and commit the resulting tree.564      lanes = getLanesToRetrySynchronouslyOnError(root);565      if (lanes !== NoLanes) {566        exitStatus = renderRootSync(root, lanes);567      }568    }569    if (exitStatus === RootFatalErrored) {570      var fatalError = workInProgressRootFatalError;571      prepareFreshStack(root, NoLanes);572      markRootSuspended$1(root, lanes);573      ensureRootIsScheduled(root, now());574      throw fatalError;575    } // We now have a consistent tree. Because this is a sync render, we576    // will commit it even if something suspended.577    var finishedWork = root.current.alternate;578    root.finishedWork = finishedWork;579    root.finishedLanes = lanes;580    commitRoot(root); // Before exiting, make sure there's a callback scheduled for the next581    // pending level.582    ensureRootIsScheduled(root, now());583    return null;584  }585  function flushRoot(root, lanes) {586    markRootExpired(root, lanes);587    ensureRootIsScheduled(root, now());588    if ((executionContext & (RenderContext | CommitContext)) === NoContext) {589      resetRenderTimer();590      flushSyncCallbackQueue();591    }592  }593  function getExecutionContext() {594    return executionContext;595  }596  // flush reactäºä»¶597  function flushDiscreteUpdates() {598    // TODO: Should be able to flush inside batchedUpdates, but not inside `act`.599    // However, `act` uses `batchedUpdates`, so there's no way to distinguish600    // those two cases. Need to fix this before exposing flushDiscreteUpdates601    // as a public API.602    if ((executionContext & (BatchedContext | RenderContext | CommitContext)) !== NoContext) {603      {604        if ((executionContext & RenderContext) !== NoContext) {605          error('unstable_flushDiscreteUpdates: Cannot flush updates when React is ' + 'already rendering.');606        }607      } // We're already rendering, so we can't synchronously flush pending work.608      // This is probably a nested event dispatch triggered by a lifecycle/effect,609      // like `el.focus()`. Exit.610      return;611    }612    flushPendingDiscreteUpdates(); // If the discrete updates scheduled passive effects, flush them now so that613    // they fire before the next serial event.614    flushPassiveEffects();615  }616  function flushPendingDiscreteUpdates() {617    if (rootsWithPendingDiscreteUpdates !== null) {618      // For each root with pending discrete updates, schedule a callback to619      // immediately flush them.620      var roots = rootsWithPendingDiscreteUpdates;621      rootsWithPendingDiscreteUpdates = null;622      roots.forEach(function (root) {623        markDiscreteUpdatesExpired(root);624        ensureRootIsScheduled(root, now());625      });626    } // Now flush the immediate queue.627    flushSyncCallbackQueue();628  }629  function batchedUpdates$1(fn, a) {630    var prevExecutionContext = executionContext;631    executionContext |= BatchedContext;632    try {633      return fn(a);634    } finally {635      executionContext = prevExecutionContext;636      if (executionContext === NoContext) {637        // Flush the immediate callbacks that were scheduled during this batch638        resetRenderTimer();639        flushSyncCallbackQueue();640      }641    }642  }643  function batchedEventUpdates$1(fn, a) {644    var prevExecutionContext = executionContext;645    executionContext |= EventContext;646    try {647      return fn(a);648    } finally {649      executionContext = prevExecutionContext;650      if (executionContext === NoContext) {651        // Flush the immediate callbacks that were scheduled during this batch652        resetRenderTimer();653        flushSyncCallbackQueue();654      }655    }656  }657  function discreteUpdates$1(fn, a, b, c, d) {658    var prevExecutionContext = executionContext;659    executionContext |= DiscreteEventContext;660    {661      try {662        return runWithPriority$1(UserBlockingPriority$2, fn.bind(null, a, b, c, d));663      } finally {664        executionContext = prevExecutionContext;665        if (executionContext === NoContext) {666          // Flush the immediate callbacks that were scheduled during this batch667          resetRenderTimer();668          flushSyncCallbackQueue();669        }670      }671    }672  }673  // 鿹鿴æ°674  function unbatchedUpdates(fn, a) {675    var prevExecutionContext = executionContext;676    executionContext &= ~BatchedContext;677    executionContext |= LegacyUnbatchedContext;678    try {679      return fn(a);680    } finally {681      executionContext = prevExecutionContext;682      if (executionContext === NoContext) {683        // Flush the immediate callbacks that were scheduled during this batch684        resetRenderTimer();685        flushSyncCallbackQueue();686      }687    }688  }689  function flushSync(fn, a) {690    var prevExecutionContext = executionContext;691    if ((prevExecutionContext & (RenderContext | CommitContext)) !== NoContext) {692      {693        error('flushSync was called from inside a lifecycle method. React cannot ' + 'flush when React is already rendering. Consider moving this call to ' + 'a scheduler task or micro task.');694      }695      return fn(a);696    }697    executionContext |= BatchedContext;698    {699      try {700        if (fn) {701          return runWithPriority$1(ImmediatePriority$1, fn.bind(null, a));702        } else {703          return undefined;704        }705      } finally {706        executionContext = prevExecutionContext; // Flush the immediate callbacks that were scheduled during this batch.707        // Note that this will happen even if batchedUpdates is higher up708        // the stack.709        flushSyncCallbackQueue();710      }711    }712  }713  function flushControlled(fn) {714    var prevExecutionContext = executionContext;715    executionContext |= BatchedContext;716    {717      try {718        runWithPriority$1(ImmediatePriority$1, fn);719      } finally {720        executionContext = prevExecutionContext;721        if (executionContext === NoContext) {722          // Flush the immediate callbacks that were scheduled during this batch723          resetRenderTimer();724          flushSyncCallbackQueue();725        }726      }727    }728  }729  function pushRenderLanes(fiber, lanes) {730    push(subtreeRenderLanesCursor, subtreeRenderLanes, fiber);731    subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);732    workInProgressRootIncludedLanes = mergeLanes(workInProgressRootIncludedLanes, lanes);733  }734  function popRenderLanes(fiber) {735    subtreeRenderLanes = subtreeRenderLanesCursor.current;736    pop(subtreeRenderLanesCursor, fiber);737  }738  function prepareFreshStack(root, lanes) {739    root.finishedWork = null;740    root.finishedLanes = NoLanes;741    var timeoutHandle = root.timeoutHandle;742    if (timeoutHandle !== noTimeout) {743      // The root previous suspended and scheduled a timeout to commit a fallback744      // state. Now that we have additional work, cancel the timeout.745      root.timeoutHandle = noTimeout; // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above746      cancelTimeout(timeoutHandle);747    }748    if (workInProgress !== null) {749      var interruptedWork = workInProgress.return;750      while (interruptedWork !== null) {751        unwindInterruptedWork(interruptedWork);752        interruptedWork = interruptedWork.return;753      }754    }755    workInProgressRoot = root;756    workInProgress = createWorkInProgress(root.current, null);757    workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;758    workInProgressRootExitStatus = RootIncomplete;759    workInProgressRootFatalError = null;760    workInProgressRootSkippedLanes = NoLanes;761    workInProgressRootUpdatedLanes = NoLanes;762    workInProgressRootPingedLanes = NoLanes;763    {764      spawnedWorkDuringRender = null;765    }766    {767      ReactStrictModeWarnings.discardPendingWarnings();768    }769  }770  function handleError(root, thrownValue) {771    do {772      var erroredWork = workInProgress;773      try {774        // Reset module-level state that was set during the render phase.775        resetContextDependencies();776        resetHooksAfterThrow();777        resetCurrentFiber(); // TODO: I found and added this missing line while investigating a778        // separate issue. Write a regression test using string refs.779        ReactCurrentOwner$2.current = null;780        if (erroredWork === null || erroredWork.return === null) {781          // Expected to be working on a non-root fiber. This is a fatal error782          // because there's no ancestor that can handle it; the root is783          // supposed to capture all errors that weren't caught by an error784          // boundary.785          workInProgressRootExitStatus = RootFatalErrored;786          workInProgressRootFatalError = thrownValue; // Set `workInProgress` to null. This represents advancing to the next787          // sibling, or the parent if there are no siblings. But since the root788          // has no siblings nor a parent, we set it to null. Usually this is789          // handled by `completeUnitOfWork` or `unwindWork`, but since we're790          // intentionally not calling those, we need set it here.791          // TODO: Consider calling `unwindWork` to pop the contexts.792          workInProgress = null;793          return;794        }795        if (enableProfilerTimer && erroredWork.mode & ProfileMode) {796          // Record the time spent rendering before an error was thrown. This797          // avoids inaccurate Profiler durations in the case of a798          // suspended render.799          stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);800        }801        throwException(root, erroredWork.return, erroredWork, thrownValue, workInProgressRootRenderLanes);802        completeUnitOfWork(erroredWork);803      } catch (yetAnotherThrownValue) {804        // Something in the return path also threw.805        thrownValue = yetAnotherThrownValue;806        if (workInProgress === erroredWork && erroredWork !== null) {807          // If this boundary has already errored, then we had trouble processing808          // the error. Bubble it to the next boundary.809          erroredWork = erroredWork.return;810          workInProgress = erroredWork;811        } else {812          erroredWork = workInProgress;813        }814        continue;815      } // Return to the normal work loop.816      return;817    } while (true);818  }819  function pushDispatcher() {820    var prevDispatcher = ReactCurrentDispatcher$2.current;821    ReactCurrentDispatcher$2.current = ContextOnlyDispatcher;822    if (prevDispatcher === null) {823      // The React isomorphic package does not include a default dispatcher.824      // Instead the first renderer will lazily attach one, in order to give825      // nicer error messages.826      return ContextOnlyDispatcher;827    } else {828      return prevDispatcher;829    }830  }831  function popDispatcher(prevDispatcher) {832    ReactCurrentDispatcher$2.current = prevDispatcher;833  }834  function pushInteractions(root) {835    {836      var prevInteractions = __interactionsRef.current;837      __interactionsRef.current = root.memoizedInteractions;838      return prevInteractions;839    }840  }841  function popInteractions(prevInteractions) {842    {843      __interactionsRef.current = prevInteractions;844    }845  }846  function markCommitTimeOfFallback() {847    globalMostRecentFallbackTime = now();848  }849  function markSkippedUpdateLanes(lane) {850    workInProgressRootSkippedLanes = mergeLanes(lane, workInProgressRootSkippedLanes);851  }852  function renderDidSuspend() {853    if (workInProgressRootExitStatus === RootIncomplete) {854      workInProgressRootExitStatus = RootSuspended;855    }856  }857  function renderDidSuspendDelayIfPossible() {858    if (workInProgressRootExitStatus === RootIncomplete || workInProgressRootExitStatus === RootSuspended) {859      workInProgressRootExitStatus = RootSuspendedWithDelay;860    } // Check if there are updates that we skipped tree that might have unblocked861    // this render.862    if (workInProgressRoot !== null && (includesNonIdleWork(workInProgressRootSkippedLanes) || includesNonIdleWork(workInProgressRootUpdatedLanes))) {863      // Mark the current render as suspended so that we switch to working on864      // the updates that were skipped. Usually we only suspend at the end of865      // the render phase.866      // TODO: We should probably always mark the root as suspended immediately867      // (inside this function), since by suspending at the end of the render868      // phase introduces a potential mistake where we suspend lanes that were869      // pinged or updated while we were rendering.870      markRootSuspended$1(workInProgressRoot, workInProgressRootRenderLanes);871    }872  }873  function renderDidError() {874    if (workInProgressRootExitStatus !== RootCompleted) {875      workInProgressRootExitStatus = RootErrored;876    }877  } // Called during render to determine if anything has suspended.878  // Returns false if we're not sure.879  function renderHasNotSuspendedYet() {880    // If something errored or completed, we can't really be sure,881    // so those are false.882    return workInProgressRootExitStatus === RootIncomplete;883  }884  function renderRootSync(root, lanes) {885    var prevExecutionContext = executionContext;886    executionContext |= RenderContext;887    var prevDispatcher = pushDispatcher(); // If the root or lanes have changed, throw out the existing stack888    // and prepare a fresh one. Otherwise we'll continue where we left off.889    if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {890      prepareFreshStack(root, lanes);891      startWorkOnPendingInteractions(root, lanes);892    }893    var prevInteractions = pushInteractions(root);894    {895      markRenderStarted(lanes);896    }897    do {898      try {899        workLoopSync();900        break;901      } catch (thrownValue) {902        handleError(root, thrownValue);903      }904    } while (true);905    resetContextDependencies();906    {907      popInteractions(prevInteractions);908    }909    executionContext = prevExecutionContext;910    popDispatcher(prevDispatcher);911    if (workInProgress !== null) {912      // This is a sync render, so we should have finished the whole tree.913      {914        {915          throw Error( "Cannot commit an incomplete root. This error is likely caused by a bug in React. Please file an issue." );916        }917      }918    }919    {920      markRenderStopped();921    } // Set this to null to indicate there's no in-progress render.922    workInProgressRoot = null;923    workInProgressRootRenderLanes = NoLanes;924    return workInProgressRootExitStatus;925  } // The work loop is an extremely hot path. Tell Closure not to inline it.926  /** @noinline */927  function workLoopSync() {928    // Already timed out, so perform work without checking if we need to yield.929    while (workInProgress !== null) {930      performUnitOfWork(workInProgress);931    }932  }933  function renderRootConcurrent(root, lanes) {934    var prevExecutionContext = executionContext;935    executionContext |= RenderContext;936    var prevDispatcher = pushDispatcher(); // If the root or lanes have changed, throw out the existing stack937    // and prepare a fresh one. Otherwise we'll continue where we left off.938    if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {939      resetRenderTimer();940      prepareFreshStack(root, lanes);941      startWorkOnPendingInteractions(root, lanes);942    }943    var prevInteractions = pushInteractions(root);944    {945      markRenderStarted(lanes);946    }947    do {948      try {949        workLoopConcurrent();950        break;951      } catch (thrownValue) {952        handleError(root, thrownValue);953      }954    } while (true);955    resetContextDependencies();956    {957      popInteractions(prevInteractions);958    }959    popDispatcher(prevDispatcher);960    executionContext = prevExecutionContext;961    if (workInProgress !== null) {962      // Still work remaining.963      {964        markRenderYielded();965      }966      return RootIncomplete;967    } else {968      // Completed the tree.969      {970        markRenderStopped();971      } // Set this to null to indicate there's no in-progress render.972      workInProgressRoot = null;973      workInProgressRootRenderLanes = NoLanes; // Return the final exit status.974      return workInProgressRootExitStatus;975    }976  }977  /** @noinline */978  function workLoopConcurrent() {979    // Perform work until Scheduler asks us to yield980    while (workInProgress !== null && !shouldYield()) {981      performUnitOfWork(workInProgress);982    }983  }984  function performUnitOfWork(unitOfWork) {985    // The current, flushed, state of this fiber is the alternate. Ideally986    // nothing should rely on this, but relying on it here means that we don't987    // need an additional field on the work in progress.988    var current = unitOfWork.alternate;989    setCurrentFiber(unitOfWork);990    var next;991    if ( (unitOfWork.mode & ProfileMode) !== NoMode) {992      startProfilerTimer(unitOfWork);993      next = beginWork$1(current, unitOfWork, subtreeRenderLanes);994      stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);995    } else {996      next = beginWork$1(current, unitOfWork, subtreeRenderLanes);997    }998    resetCurrentFiber();999    unitOfWork.memoizedProps = unitOfWork.pendingProps;1000    if (next === null) {1001      // If this doesn't spawn new work, complete the current work.1002      completeUnitOfWork(unitOfWork);1003    } else {1004      workInProgress = next;1005    }1006    ReactCurrentOwner$2.current = null;1007  }1008  function completeUnitOfWork(unitOfWork) {1009    // Attempt to complete the current unit of work, then move to the next1010    // sibling. If there are no more siblings, return to the parent fiber.1011    var completedWork = unitOfWork;1012    do {1013      // The current, flushed, state of this fiber is the alternate. Ideally1014      // nothing should rely on this, but relying on it here means that we don't1015      // need an additional field on the work in progress.1016      var current = completedWork.alternate;1017      var returnFiber = completedWork.return; // Check if the work completed or if something threw.1018      if ((completedWork.flags & Incomplete) === NoFlags) {1019        setCurrentFiber(completedWork);1020        var next = void 0;1021        if ( (completedWork.mode & ProfileMode) === NoMode) {1022          next = completeWork(current, completedWork, subtreeRenderLanes);1023        } else {1024          startProfilerTimer(completedWork);1025          next = completeWork(current, completedWork, subtreeRenderLanes); // Update render duration assuming we didn't error.1026          stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1027        }1028        resetCurrentFiber();1029        if (next !== null) {1030          // Completing this fiber spawned new work. Work on that next.1031          workInProgress = next;1032          return;1033        }1034        resetChildLanes(completedWork);1035        if (returnFiber !== null && // Do not append effects to parents if a sibling failed to complete1036        (returnFiber.flags & Incomplete) === NoFlags) {1037          // Append all the effects of the subtree and this fiber onto the effect1038          // list of the parent. The completion order of the children affects the1039          // side-effect order.1040          if (returnFiber.firstEffect === null) {1041            returnFiber.firstEffect = completedWork.firstEffect;1042          }1043          if (completedWork.lastEffect !== null) {1044            if (returnFiber.lastEffect !== null) {1045              returnFiber.lastEffect.nextEffect = completedWork.firstEffect;1046            }1047            returnFiber.lastEffect = completedWork.lastEffect;1048          } // If this fiber had side-effects, we append it AFTER the children's1049          // side-effects. We can perform certain side-effects earlier if needed,1050          // by doing multiple passes over the effect list. We don't want to1051          // schedule our own side-effect on our own list because if end up1052          // reusing children we'll schedule this effect onto itself since we're1053          // at the end.1054          var flags = completedWork.flags; // Skip both NoWork and PerformedWork tags when creating the effect1055          // list. PerformedWork effect is read by React DevTools but shouldn't be1056          // committed.1057          if (flags > PerformedWork) {1058            if (returnFiber.lastEffect !== null) {1059              returnFiber.lastEffect.nextEffect = completedWork;1060            } else {1061              returnFiber.firstEffect = completedWork;1062            }1063            returnFiber.lastEffect = completedWork;1064          }1065        }1066      } else {1067        // This fiber did not complete because something threw. Pop values off1068        // the stack without entering the complete phase. If this is a boundary,1069        // capture values if possible.1070        var _next = unwindWork(completedWork); // Because this fiber did not complete, don't reset its expiration time.1071        if (_next !== null) {1072          // If completing this work spawned new work, do that next. We'll come1073          // back here again.1074          // Since we're restarting, remove anything that is not a host effect1075          // from the effect tag.1076          _next.flags &= HostEffectMask;1077          workInProgress = _next;1078          return;1079        }1080        if ( (completedWork.mode & ProfileMode) !== NoMode) {1081          // Record the render duration for the fiber that errored.1082          stopProfilerTimerIfRunningAndRecordDelta(completedWork, false); // Include the time spent working on failed children before continuing.1083          var actualDuration = completedWork.actualDuration;1084          var child = completedWork.child;1085          while (child !== null) {1086            actualDuration += child.actualDuration;1087            child = child.sibling;1088          }1089          completedWork.actualDuration = actualDuration;1090        }1091        if (returnFiber !== null) {1092          // Mark the parent fiber as incomplete and clear its effect list.1093          returnFiber.firstEffect = returnFiber.lastEffect = null;1094          returnFiber.flags |= Incomplete;1095        }1096      }1097      var siblingFiber = completedWork.sibling;1098      if (siblingFiber !== null) {1099        // If there is more work to do in this returnFiber, do that next.1100        workInProgress = siblingFiber;1101        return;1102      } // Otherwise, return to the parent1103      completedWork = returnFiber; // Update the next thing we're working on in case something throws.1104      workInProgress = completedWork;1105    } while (completedWork !== null); // We've reached the root.1106    if (workInProgressRootExitStatus === RootIncomplete) {1107      workInProgressRootExitStatus = RootCompleted;1108    }1109  }1110  function resetChildLanes(completedWork) {1111    if ( // TODO: Move this check out of the hot path by moving `resetChildLanes`1112    // to switch statement in `completeWork`.1113    (completedWork.tag === LegacyHiddenComponent || completedWork.tag === OffscreenComponent) && completedWork.memoizedState !== null && !includesSomeLane(subtreeRenderLanes, OffscreenLane) && (completedWork.mode & ConcurrentMode) !== NoLanes) {1114      // The children of this component are hidden. Don't bubble their1115      // expiration times.1116      return;1117    }1118    var newChildLanes = NoLanes; // Bubble up the earliest expiration time.1119    if ( (completedWork.mode & ProfileMode) !== NoMode) {1120      // In profiling mode, resetChildExpirationTime is also used to reset1121      // profiler durations.1122      var actualDuration = completedWork.actualDuration;1123      var treeBaseDuration = completedWork.selfBaseDuration; // When a fiber is cloned, its actualDuration is reset to 0. This value will1124      // only be updated if work is done on the fiber (i.e. it doesn't bailout).1125      // When work is done, it should bubble to the parent's actualDuration. If1126      // the fiber has not been cloned though, (meaning no work was done), then1127      // this value will reflect the amount of time spent working on a previous1128      // render. In that case it should not bubble. We determine whether it was1129      // cloned by comparing the child pointer.1130      var shouldBubbleActualDurations = completedWork.alternate === null || completedWork.child !== completedWork.alternate.child;1131      var child = completedWork.child;1132      while (child !== null) {1133        newChildLanes = mergeLanes(newChildLanes, mergeLanes(child.lanes, child.childLanes));1134        if (shouldBubbleActualDurations) {1135          actualDuration += child.actualDuration;1136        }1137        treeBaseDuration += child.treeBaseDuration;1138        child = child.sibling;1139      }1140      var isTimedOutSuspense = completedWork.tag === SuspenseComponent && completedWork.memoizedState !== null;1141      if (isTimedOutSuspense) {1142        // Don't count time spent in a timed out Suspense subtree as part of the base duration.1143        var primaryChildFragment = completedWork.child;1144        if (primaryChildFragment !== null) {1145          treeBaseDuration -= primaryChildFragment.treeBaseDuration;1146        }1147      }1148      completedWork.actualDuration = actualDuration;1149      completedWork.treeBaseDuration = treeBaseDuration;1150    } else {1151      var _child = completedWork.child;1152      while (_child !== null) {1153        newChildLanes = mergeLanes(newChildLanes, mergeLanes(_child.lanes, _child.childLanes));1154        _child = _child.sibling;1155      }1156    }1157    completedWork.childLanes = newChildLanes;1158  }1159  function commitRoot(root) {1160    var renderPriorityLevel = getCurrentPriorityLevel();1161    runWithPriority$1(ImmediatePriority$1, commitRootImpl.bind(null, root, renderPriorityLevel));1162    return null;1163  }1164  function commitRootImpl(root, renderPriorityLevel) {1165    do {1166      // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which1167      // means `flushPassiveEffects` will sometimes result in additional1168      // passive effects. So we need to keep flushing in a loop until there are1169      // no more pending effects.1170      // TODO: Might be better if `flushPassiveEffects` did not automatically1171      // flush synchronous work at the end, to avoid factoring hazards like this.1172      flushPassiveEffects();1173    } while (rootWithPendingPassiveEffects !== null);1174    flushRenderPhaseStrictModeWarningsInDEV();1175    if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {1176      {1177        throw Error( "Should not already be working." );1178      }1179    }1180    var finishedWork = root.finishedWork;1181    var lanes = root.finishedLanes;1182    {1183      markCommitStarted(lanes);1184    }1185    if (finishedWork === null) {1186      {1187        markCommitStopped();1188      }1189      return null;1190    }1191    root.finishedWork = null;1192    root.finishedLanes = NoLanes;1193    if (!(finishedWork !== root.current)) {1194      {1195        throw Error( "Cannot commit the same tree as before. This error is likely caused by a bug in React. Please file an issue." );1196      }1197    } // commitRoot never returns a continuation; it always finishes synchronously.1198    // So we can clear these now to allow a new callback to be scheduled.1199    root.callbackNode = null; // Update the first and last pending times on this root. The new first1200    // pending time is whatever is left on the root fiber.1201    var remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);1202    markRootFinished(root, remainingLanes); // Clear already finished discrete updates in case that a later call of1203    // `flushDiscreteUpdates` starts a useless render pass which may cancels1204    // a scheduled timeout.1205    if (rootsWithPendingDiscreteUpdates !== null) {1206      if (!hasDiscreteLanes(remainingLanes) && rootsWithPendingDiscreteUpdates.has(root)) {1207        rootsWithPendingDiscreteUpdates.delete(root);1208      }1209    }1210    if (root === workInProgressRoot) {1211      // We can reset these now that they are finished.1212      workInProgressRoot = null;1213      workInProgress = null;1214      workInProgressRootRenderLanes = NoLanes;1215    } // Get the list of effects.1216    var firstEffect;1217    if (finishedWork.flags > PerformedWork) {1218      // A fiber's effect list consists only of its children, not itself. So if1219      // the root has an effect, we need to add it to the end of the list. The1220      // resulting list is the set that would belong to the root's parent, if it1221      // had one; that is, all the effects in the tree including the root.1222      if (finishedWork.lastEffect !== null) {1223        finishedWork.lastEffect.nextEffect = finishedWork;1224        firstEffect = finishedWork.firstEffect;1225      } else {1226        firstEffect = finishedWork;1227      }1228    } else {1229      // There is no effect on the root.1230      firstEffect = finishedWork.firstEffect;1231    }1232    if (firstEffect !== null) {1233      var prevExecutionContext = executionContext;1234      executionContext |= CommitContext;1235      var prevInteractions = pushInteractions(root); // Reset this to null before calling lifecycles1236      ReactCurrentOwner$2.current = null; // The commit phase is broken into several sub-phases. We do a separate pass1237      // of the effect list for each phase: all mutation effects come before all1238      // layout effects, and so on.1239      // The first phase a "before mutation" phase. We use this phase to read the1240      // state of the host tree right before we mutate it. This is where1241      // getSnapshotBeforeUpdate is called.1242      focusedInstanceHandle = prepareForCommit(root.containerInfo);1243      shouldFireAfterActiveInstanceBlur = false;1244      nextEffect = firstEffect;1245      do {1246        {1247          invokeGuardedCallback(null, commitBeforeMutationEffects, null);1248          if (hasCaughtError()) {1249            if (!(nextEffect !== null)) {1250              {1251                throw Error( "Should be working on an effect." );1252              }1253            }1254            var error = clearCaughtError();1255            captureCommitPhaseError(nextEffect, error);1256            nextEffect = nextEffect.nextEffect;1257          }1258        }1259      } while (nextEffect !== null); // We no longer need to track the active instance fiber1260      focusedInstanceHandle = null;1261      {1262        // Mark the current commit time to be shared by all Profilers in this1263        // batch. This enables them to be grouped later.1264        recordCommitTime();1265      } // The next phase is the mutation phase, where we mutate the host tree.1266      nextEffect = firstEffect;1267      do {1268        {1269          invokeGuardedCallback(null, commitMutationEffects, null, root, renderPriorityLevel);1270          if (hasCaughtError()) {1271            if (!(nextEffect !== null)) {1272              {1273                throw Error( "Should be working on an effect." );1274              }1275            }1276            var _error = clearCaughtError();1277            captureCommitPhaseError(nextEffect, _error);1278            nextEffect = nextEffect.nextEffect;1279          }1280        }1281      } while (nextEffect !== null);1282      resetAfterCommit(root.containerInfo); // The work-in-progress tree is now the current tree. This must come after1283      // the mutation phase, so that the previous tree is still current during1284      // componentWillUnmount, but before the layout phase, so that the finished1285      // work is current during componentDidMount/Update.1286      root.current = finishedWork; // The next phase is the layout phase, where we call effects that read1287      // the host tree after it's been mutated. The idiomatic use case for this is1288      // layout, but class component lifecycles also fire here for legacy reasons.1289      nextEffect = firstEffect;1290      do {1291        {1292          invokeGuardedCallback(null, commitLayoutEffects, null, root, lanes);1293          if (hasCaughtError()) {1294            if (!(nextEffect !== null)) {1295              {1296                throw Error( "Should be working on an effect." );1297              }1298            }1299            var _error2 = clearCaughtError();1300            captureCommitPhaseError(nextEffect, _error2);1301            nextEffect = nextEffect.nextEffect;1302          }1303        }1304      } while (nextEffect !== null);1305      nextEffect = null; // Tell Scheduler to yield at the end of the frame, so the browser has an1306      // opportunity to paint.1307      requestPaint();1308      {1309        popInteractions(prevInteractions);1310      }1311      executionContext = prevExecutionContext;1312    } else {1313      // No effects.1314      root.current = finishedWork; // Measure these anyway so the flamegraph explicitly shows that there were1315      // no effects.1316      // TODO: Maybe there's a better way to report this.1317      {1318        recordCommitTime();1319      }1320    }1321    var rootDidHavePassiveEffects = rootDoesHavePassiveEffects;1322    if (rootDoesHavePassiveEffects) {1323      // This commit has passive effects. Stash a reference to them. But don't1324      // schedule a callback until after flushing layout work.1325      rootDoesHavePassiveEffects = false;1326      rootWithPendingPassiveEffects = root;1327      pendingPassiveEffectsLanes = lanes;1328      pendingPassiveEffectsRenderPriority = renderPriorityLevel;1329    } else {1330      // We are done with the effect chain at this point so let's clear the1331      // nextEffect pointers to assist with GC. If we have passive effects, we'll1332      // clear this in flushPassiveEffects.1333      nextEffect = firstEffect;1334      while (nextEffect !== null) {1335        var nextNextEffect = nextEffect.nextEffect;1336        nextEffect.nextEffect = null;1337        if (nextEffect.flags & Deletion) {1338          detachFiberAfterEffects(nextEffect);1339        }1340        nextEffect = nextNextEffect;1341      }1342    } // Read this again, since an effect might have updated it1343    remainingLanes = root.pendingLanes; // Check if there's remaining work on this root1344    if (remainingLanes !== NoLanes) {1345      {1346        if (spawnedWorkDuringRender !== null) {1347          var expirationTimes = spawnedWorkDuringRender;1348          spawnedWorkDuringRender = null;1349          for (var i = 0; i < expirationTimes.length; i++) {1350            scheduleInteractions(root, expirationTimes[i], root.memoizedInteractions);1351          }1352        }1353        schedulePendingInteractions(root, remainingLanes);1354      }1355    } else {1356      // If there's no remaining work, we can clear the set of already failed1357      // error boundaries.1358      legacyErrorBoundariesThatAlreadyFailed = null;1359    }1360    {1361      if (!rootDidHavePassiveEffects) {1362        // If there are no passive effects, then we can complete the pending interactions.1363        // Otherwise, we'll wait until after the passive effects are flushed.1364        // Wait to do this until after remaining work has been scheduled,1365        // so that we don't prematurely signal complete for interactions when there's e.g. hidden work.1366        finishPendingInteractions(root, lanes);1367      }1368    }1369    if (remainingLanes === SyncLane) {1370      // Count the number of times the root synchronously re-renders without1371      // finishing. If there are too many, it indicates an infinite update loop.1372      if (root === rootWithNestedUpdates) {1373        nestedUpdateCount++;1374      } else {1375        nestedUpdateCount = 0;1376        rootWithNestedUpdates = root;1377      }1378    } else {1379      nestedUpdateCount = 0;1380    }1381    onCommitRoot(finishedWork.stateNode, renderPriorityLevel);1382    {1383      onCommitRoot$1();1384    } // Always call this before exiting `commitRoot`, to ensure that any1385    // additional work on this root is scheduled.1386    ensureRootIsScheduled(root, now());1387    if (hasUncaughtError) {1388      hasUncaughtError = false;1389      var _error3 = firstUncaughtError;1390      firstUncaughtError = null;1391      throw _error3;1392    }1393    if ((executionContext & LegacyUnbatchedContext) !== NoContext) {1394      {1395        markCommitStopped();1396      } // This is a legacy edge case. We just committed the initial mount of1397      // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1398      // synchronously, but layout updates should be deferred until the end1399      // of the batch.1400      return null;1401    } // If layout work was scheduled, flush it now.1402    flushSyncCallbackQueue();1403    {1404      markCommitStopped();1405    }1406    return null;1407  }1408  function commitBeforeMutationEffects() {1409    while (nextEffect !== null) {1410      var current = nextEffect.alternate;1411      if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {1412        if ((nextEffect.flags & Deletion) !== NoFlags) {1413          if (doesFiberContain(nextEffect, focusedInstanceHandle)) {1414            shouldFireAfterActiveInstanceBlur = true;1415          }1416        } else {1417          // TODO: Move this out of the hot path using a dedicated effect tag.1418          if (nextEffect.tag === SuspenseComponent && isSuspenseBoundaryBeingHidden(current, nextEffect) && doesFiberContain(nextEffect, focusedInstanceHandle)) {1419            shouldFireAfterActiveInstanceBlur = true;1420          }1421        }1422      }1423      var flags = nextEffect.flags;1424      if ((flags & Snapshot) !== NoFlags) {1425        setCurrentFiber(nextEffect);1426        commitBeforeMutationLifeCycles(current, nextEffect);1427        resetCurrentFiber();1428      }1429      if ((flags & Passive) !== NoFlags) {1430        // If there are passive effects, schedule a callback to flush at1431        // the earliest opportunity.1432        if (!rootDoesHavePassiveEffects) {1433          rootDoesHavePassiveEffects = true;1434          scheduleCallback(NormalPriority$1, function () {1435            flushPassiveEffects();1436            return null;1437          });1438        }1439      }1440      nextEffect = nextEffect.nextEffect;1441    }1442  }1443  function commitMutationEffects(root, renderPriorityLevel) {1444    // TODO: Should probably move the bulk of this function to commitWork.1445    while (nextEffect !== null) {1446      setCurrentFiber(nextEffect);1447      var flags = nextEffect.flags;1448      if (flags & ContentReset) {1449        commitResetTextContent(nextEffect);1450      }1451      if (flags & Ref) {1452        var current = nextEffect.alternate;1453        if (current !== null) {1454          commitDetachRef(current);1455        }1456      } // The following switch statement is only concerned about placement,1457      // updates, and deletions. To avoid needing to add a case for every possible1458      // bitmap value, we remove the secondary effects from the effect tag and1459      // switch on that value.1460      var primaryFlags = flags & (Placement | Update | Deletion | Hydrating);1461      switch (primaryFlags) {1462        case Placement:1463          {1464            commitPlacement(nextEffect); // Clear the "placement" from effect tag so that we know that this is1465            // inserted, before any life-cycles like componentDidMount gets called.1466            // TODO: findDOMNode doesn't rely on this any more but isMounted does1467            // and isMounted is deprecated anyway so we should be able to kill this.1468            nextEffect.flags &= ~Placement;1469            break;1470          }1471        case PlacementAndUpdate:1472          {1473            // Placement1474            commitPlacement(nextEffect); // Clear the "placement" from effect tag so that we know that this is1475            // inserted, before any life-cycles like componentDidMount gets called.1476            nextEffect.flags &= ~Placement; // Update1477            var _current = nextEffect.alternate;1478            commitWork(_current, nextEffect);1479            break;1480          }1481        case Hydrating:1482          {1483            nextEffect.flags &= ~Hydrating;1484            break;1485          }1486        case HydratingAndUpdate:1487          {1488            nextEffect.flags &= ~Hydrating; // Update1489            var _current2 = nextEffect.alternate;1490            commitWork(_current2, nextEffect);1491            break;1492          }1493        case Update:1494          {1495            var _current3 = nextEffect.alternate;1496            commitWork(_current3, nextEffect);1497            break;1498          }1499        case Deletion:1500          {1501            commitDeletion(root, nextEffect);1502            break;1503          }1504      }1505      resetCurrentFiber();1506      nextEffect = nextEffect.nextEffect;1507    }1508  }1509  function commitLayoutEffects(root, committedLanes) {1510    {1511      markLayoutEffectsStarted(committedLanes);1512    } // TODO: Should probably move the bulk of this function to commitWork.1513    while (nextEffect !== null) {1514      setCurrentFiber(nextEffect);1515      var flags = nextEffect.flags;1516      if (flags & (Update | Callback)) {1517        var current = nextEffect.alternate;1518        commitLifeCycles(root, current, nextEffect);1519      }1520      {1521        if (flags & Ref) {1522          commitAttachRef(nextEffect);1523        }1524      }1525      resetCurrentFiber();1526      nextEffect = nextEffect.nextEffect;1527    }1528    {1529      markLayoutEffectsStopped();1530    }1531  }1532  function flushPassiveEffects() {1533    // Returns whether passive effects were flushed.1534    if (pendingPassiveEffectsRenderPriority !== NoPriority$1) {1535      var priorityLevel = pendingPassiveEffectsRenderPriority > NormalPriority$1 ? NormalPriority$1 : pendingPassiveEffectsRenderPriority;1536      pendingPassiveEffectsRenderPriority = NoPriority$1;1537      {1538        return runWithPriority$1(priorityLevel, flushPassiveEffectsImpl);1539      }1540    }1541    return false;1542  }1543  function enqueuePendingPassiveHookEffectMount(fiber, effect) {1544    pendingPassiveHookEffectsMount.push(effect, fiber);1545    if (!rootDoesHavePassiveEffects) {1546      rootDoesHavePassiveEffects = true;1547      scheduleCallback(NormalPriority$1, function () {1548        flushPassiveEffects();1549        return null;1550      });1551    }1552  }1553  function enqueuePendingPassiveHookEffectUnmount(fiber, effect) {1554    pendingPassiveHookEffectsUnmount.push(effect, fiber);1555    {1556      fiber.flags |= PassiveUnmountPendingDev;1557      var alternate = fiber.alternate;1558      if (alternate !== null) {1559        alternate.flags |= PassiveUnmountPendingDev;1560      }1561    }1562    if (!rootDoesHavePassiveEffects) {1563      rootDoesHavePassiveEffects = true;1564      scheduleCallback(NormalPriority$1, function () {1565        flushPassiveEffects();1566        return null;1567      });1568    }1569  }1570  function invokePassiveEffectCreate(effect) {1571    var create = effect.create;1572    effect.destroy = create();1573  }1574  function flushPassiveEffectsImpl() {1575    if (rootWithPendingPassiveEffects === null) {1576      return false;1577    }1578    var root = rootWithPendingPassiveEffects;1579    var lanes = pendingPassiveEffectsLanes;1580    rootWithPendingPassiveEffects = null;1581    pendingPassiveEffectsLanes = NoLanes;1582    if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {1583      {1584        throw Error( "Cannot flush passive effects while already rendering." );1585      }1586    }1587    {1588      markPassiveEffectsStarted(lanes);1589    }1590    {1591      isFlushingPassiveEffects = true;1592    }1593    var prevExecutionContext = executionContext;1594    executionContext |= CommitContext;1595    var prevInteractions = pushInteractions(root); // It's important that ALL pending passive effect destroy functions are called1596    // before ANY passive effect create functions are called.1597    // Otherwise effects in sibling components might interfere with each other.1598    // e.g. a destroy function in one component may unintentionally override a ref1599    // value set by a create function in another component.1600    // Layout effects have the same constraint.1601    // First pass: Destroy stale passive effects.1602    var unmountEffects = pendingPassiveHookEffectsUnmount;1603    pendingPassiveHookEffectsUnmount = [];1604    for (var i = 0; i < unmountEffects.length; i += 2) {1605      var _effect = unmountEffects[i];1606      var fiber = unmountEffects[i + 1];1607      var destroy = _effect.destroy;1608      _effect.destroy = undefined;1609      {1610        fiber.flags &= ~PassiveUnmountPendingDev;1611        var alternate = fiber.alternate;1612        if (alternate !== null) {1613          alternate.flags &= ~PassiveUnmountPendingDev;1614        }1615      }1616      if (typeof destroy === 'function') {1617        {1618          setCurrentFiber(fiber);1619          {1620            invokeGuardedCallback(null, destroy, null);1621          }1622          if (hasCaughtError()) {1623            if (!(fiber !== null)) {1624              {1625                throw Error( "Should be working on an effect." );1626              }1627            }1628            var error = clearCaughtError();1629            captureCommitPhaseError(fiber, error);1630          }1631          resetCurrentFiber();1632        }1633      }1634    } // Second pass: Create new passive effects.1635    var mountEffects = pendingPassiveHookEffectsMount;1636    pendingPassiveHookEffectsMount = [];1637    for (var _i = 0; _i < mountEffects.length; _i += 2) {1638      var _effect2 = mountEffects[_i];1639      var _fiber = mountEffects[_i + 1];1640      {1641        setCurrentFiber(_fiber);1642        {1643          invokeGuardedCallback(null, invokePassiveEffectCreate, null, _effect2);1644        }1645        if (hasCaughtError()) {1646          if (!(_fiber !== null)) {1647            {1648              throw Error( "Should be working on an effect." );1649            }1650          }1651          var _error4 = clearCaughtError();1652          captureCommitPhaseError(_fiber, _error4);1653        }1654        resetCurrentFiber();1655      }1656    } // Note: This currently assumes there are no passive effects on the root fiber1657    // because the root is not part of its own effect list.1658    // This could change in the future.1659    var effect = root.current.firstEffect;1660    while (effect !== null) {1661      var nextNextEffect = effect.nextEffect; // Remove nextEffect pointer to assist GC1662      effect.nextEffect = null;1663      if (effect.flags & Deletion) {1664        detachFiberAfterEffects(effect);1665      }1666      effect = nextNextEffect;1667    }1668    {1669      popInteractions(prevInteractions);1670      finishPendingInteractions(root, lanes);1671    }1672    {1673      isFlushingPassiveEffects = false;1674    }1675    {1676      markPassiveEffectsStopped();1677    }1678    executionContext = prevExecutionContext;1679    flushSyncCallbackQueue(); // If additional passive effects were scheduled, increment a counter. If this1680    // exceeds the limit, we'll fire a warning.1681    nestedPassiveUpdateCount = rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1682    return true;1683  }1684  function isAlreadyFailedLegacyErrorBoundary(instance) {1685    return legacyErrorBoundariesThatAlreadyFailed !== null && legacyErrorBoundariesThatAlreadyFailed.has(instance);1686  }1687  function markLegacyErrorBoundaryAsFailed(instance) {1688    if (legacyErrorBoundariesThatAlreadyFailed === null) {1689      legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1690    } else {1691      legacyErrorBoundariesThatAlreadyFailed.add(instance);1692    }1693  }1694  function prepareToThrowUncaughtError(error) {1695    if (!hasUncaughtError) {1696      hasUncaughtError = true;1697      firstUncaughtError = error;1698    }1699  }1700  var onUncaughtError = prepareToThrowUncaughtError;1701  function captureCommitPhaseErrorOnRoot(rootFiber, sourceFiber, error) {1702    var errorInfo = createCapturedValue(error, sourceFiber);1703    var update = createRootErrorUpdate(rootFiber, errorInfo, SyncLane);1704    enqueueUpdate(rootFiber, update);1705    var eventTime = requestEventTime();1706    var root = markUpdateLaneFromFiberToRoot(rootFiber, SyncLane);1707    if (root !== null) {1708      markRootUpdated(root, SyncLane, eventTime);1709      ensureRootIsScheduled(root, eventTime);1710      schedulePendingInteractions(root, SyncLane);1711    }1712  }1713  function captureCommitPhaseError(sourceFiber, error) {1714    if (sourceFiber.tag === HostRoot) {1715      // Error was thrown at the root. There is no parent, so the root1716      // itself should capture it.1717      captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1718      return;1719    }1720    var fiber = sourceFiber.return;1721    while (fiber !== null) {1722      if (fiber.tag === HostRoot) {1723        captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1724        return;1725      } else if (fiber.tag === ClassComponent) {1726        var ctor = fiber.type;1727        var instance = fiber.stateNode;1728        if (typeof ctor.getDerivedStateFromError === 'function' || typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance)) {1729          var errorInfo = createCapturedValue(error, sourceFiber);1730          var update = createClassErrorUpdate(fiber, errorInfo, SyncLane);1731          enqueueUpdate(fiber, update);1732          var eventTime = requestEventTime();1733          var root = markUpdateLaneFromFiberToRoot(fiber, SyncLane);1734          if (root !== null) {1735            markRootUpdated(root, SyncLane, eventTime);1736            ensureRootIsScheduled(root, eventTime);1737            schedulePendingInteractions(root, SyncLane);1738          } else {1739            // This component has already been unmounted.1740            // We can't schedule any follow up work for the root because the fiber is already unmounted,1741            // but we can still call the log-only boundary so the error isn't swallowed.1742            //1743            // TODO This is only a temporary bandaid for the old reconciler fork.1744            // We can delete this special case once the new fork is merged.1745            if (typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance)) {1746              try {1747                instance.componentDidCatch(error, errorInfo);1748              } catch (errorToIgnore) {// TODO Ignore this error? Rethrow it?1749                // This is kind of an edge case.1750              }1751            }1752          }1753          return;1754        }1755      }1756      fiber = fiber.return;1757    }1758  }1759  function pingSuspendedRoot(root, wakeable, pingedLanes) {1760    var pingCache = root.pingCache;1761    if (pingCache !== null) {1762      // The wakeable resolved, so we no longer need to memoize, because it will1763      // never be thrown again.1764      pingCache.delete(wakeable);1765    }1766    var eventTime = requestEventTime();1767    markRootPinged(root, pingedLanes);1768    if (workInProgressRoot === root && isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)) {1769      // Received a ping at the same priority level at which we're currently1770      // rendering. We might want to restart this render. This should mirror1771      // the logic of whether or not a root suspends once it completes.1772      // TODO: If we're rendering sync either due to Sync, Batched or expired,1773      // we should probably never restart.1774      // If we're suspended with delay, or if it's a retry, we'll always suspend1775      // so we can always restart.1776      if (workInProgressRootExitStatus === RootSuspendedWithDelay || workInProgressRootExitStatus === RootSuspended && includesOnlyRetries(workInProgressRootRenderLanes) && now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS) {1777        // Restart from the root.1778        prepareFreshStack(root, NoLanes);1779      } else {1780        // Even though we can't restart right now, we might get an1781        // opportunity later. So we mark this render as having a ping.1782        workInProgressRootPingedLanes = mergeLanes(workInProgressRootPingedLanes, pingedLanes);1783      }1784    }1785    ensureRootIsScheduled(root, eventTime);1786    schedulePendingInteractions(root, pingedLanes);1787  }1788  function retryTimedOutBoundary(boundaryFiber, retryLane) {1789    // The boundary fiber (a Suspense component or SuspenseList component)1790    // previously was rendered in its fallback state. One of the promises that1791    // suspended it has resolved, which means at least part of the tree was1792    // likely unblocked. Try rendering again, at a new expiration time.1793    if (retryLane === NoLane) {1794      retryLane = requestRetryLane(boundaryFiber);1795    } // TODO: Special case idle priority?1796    var eventTime = requestEventTime();1797    var root = markUpdateLaneFromFiberToRoot(boundaryFiber, retryLane);1798    if (root !== null) {1799      markRootUpdated(root, retryLane, eventTime);1800      ensureRootIsScheduled(root, eventTime);1801      schedulePendingInteractions(root, retryLane);1802    }1803  }1804  function retryDehydratedSuspenseBoundary(boundaryFiber) {1805    var suspenseState = boundaryFiber.memoizedState;1806    var retryLane = NoLane;1807    if (suspenseState !== null) {1808      retryLane = suspenseState.retryLane;1809    }1810    retryTimedOutBoundary(boundaryFiber, retryLane);1811  }1812  function resolveRetryWakeable(boundaryFiber, wakeable) {1813    var retryLane = NoLane; // Default1814    var retryCache;1815    {1816      switch (boundaryFiber.tag) {1817        case SuspenseComponent:1818          retryCache = boundaryFiber.stateNode;1819          var suspenseState = boundaryFiber.memoizedState;1820          if (suspenseState !== null) {1821            retryLane = suspenseState.retryLane;1822          }1823          break;1824        case SuspenseListComponent:1825          retryCache = boundaryFiber.stateNode;1826          break;1827        default:1828          {1829            {1830              throw Error( "Pinged unknown suspense boundary type. This is probably a bug in React." );1831            }1832          }1833      }1834    }1835    if (retryCache !== null) {1836      // The wakeable resolved, so we no longer need to memoize, because it will1837      // never be thrown again.1838      retryCache.delete(wakeable);1839    }1840    retryTimedOutBoundary(boundaryFiber, retryLane);1841  } // Computes the next Just Noticeable Difference (JND) boundary.1842  // The theory is that a person can't tell the difference between small differences in time.1843  // Therefore, if we wait a bit longer than necessary that won't translate to a noticeable1844  // difference in the experience. However, waiting for longer might mean that we can avoid1845  // showing an intermediate loading state. The longer we have already waited, the harder it1846  // is to tell small differences in time. Therefore, the longer we've already waited,1847  // the longer we can wait additionally. At some point we have to give up though.1848  // We pick a train model where the next boundary commits at a consistent schedule.1849  // These particular numbers are vague estimates. We expect to adjust them based on research.1850  function jnd(timeElapsed) {1851    return timeElapsed < 120 ? 120 : timeElapsed < 480 ? 480 : timeElapsed < 1080 ? 1080 : timeElapsed < 1920 ? 1920 : timeElapsed < 3000 ? 3000 : timeElapsed < 4320 ? 4320 : ceil(timeElapsed / 1960) * 1960;1852  }1853  function checkForNestedUpdates() {1854    if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {1855      nestedUpdateCount = 0;1856      rootWithNestedUpdates = null;1857      {1858        {1859          throw Error( "Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops." );1860        }1861      }1862    }1863    {1864      if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {1865        nestedPassiveUpdateCount = 0;1866        error('Maximum update depth exceeded. This can happen when a component ' + "calls setState inside useEffect, but useEffect either doesn't " + 'have a dependency array, or one of the dependencies changes on ' + 'every render.');1867      }1868    }1869  }1870  function flushRenderPhaseStrictModeWarningsInDEV() {1871    {1872      ReactStrictModeWarnings.flushLegacyContextWarning();1873      {1874        ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();1875      }1876    }1877  }1878  var didWarnStateUpdateForNotYetMountedComponent = null;1879  function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber) {1880    {1881      if ((executionContext & RenderContext) !== NoContext) {1882        // We let the other warning about render phase updates deal with this one.1883        return;1884      }1885      if (!(fiber.mode & (BlockingMode | ConcurrentMode))) {1886        return;1887      }1888      var tag = fiber.tag;1889      if (tag !== IndeterminateComponent && tag !== HostRoot && tag !== ClassComponent && tag !== FunctionComponent && tag !== ForwardRef && tag !== MemoComponent && tag !== SimpleMemoComponent && tag !== Block) {1890        // Only warn for user-defined components, not internal ones like Suspense.1891        return;1892      } // We show the whole stack but dedupe on the top component's name because1893      // the problematic code almost always lies inside that component.1894      var componentName = getComponentName(fiber.type) || 'ReactComponent';1895      if (didWarnStateUpdateForNotYetMountedComponent !== null) {1896        if (didWarnStateUpdateForNotYetMountedComponent.has(componentName)) {1897          return;1898        }1899        didWarnStateUpdateForNotYetMountedComponent.add(componentName);1900      } else {1901        didWarnStateUpdateForNotYetMountedComponent = new Set([componentName]);1902      }1903      var previousFiber = current;1904      try {1905        setCurrentFiber(fiber);1906        error("Can't perform a React state update on a component that hasn't mounted yet. " + 'This indicates that you have a side-effect in your render function that ' + 'asynchronously later calls tries to update the component. Move this work to ' + 'useEffect instead.');1907      } finally {1908        if (previousFiber) {1909          setCurrentFiber(fiber);1910        } else {1911          resetCurrentFiber();1912        }1913      }1914    }1915  }1916  var didWarnStateUpdateForUnmountedComponent = null;1917  function warnAboutUpdateOnUnmountedFiberInDEV(fiber) {1918    {1919      var tag = fiber.tag;1920      if (tag !== HostRoot && tag !== ClassComponent && tag !== FunctionComponent && tag !== ForwardRef && tag !== MemoComponent && tag !== SimpleMemoComponent && tag !== Block) {1921        // Only warn for user-defined components, not internal ones like Suspense.1922        return;1923      } // If there are pending passive effects unmounts for this Fiber,1924      // we can assume that they would have prevented this update.1925      if ((fiber.flags & PassiveUnmountPendingDev) !== NoFlags) {1926        return;1927      } // We show the whole stack but dedupe on the top component's name because1928      // the problematic code almost always lies inside that component.1929      var componentName = getComponentName(fiber.type) || 'ReactComponent';1930      if (didWarnStateUpdateForUnmountedComponent !== null) {1931        if (didWarnStateUpdateForUnmountedComponent.has(componentName)) {...ReactFiberScheduler.js
Source:ReactFiberScheduler.js  
...288  checkForNestedUpdates();289  warnAboutInvalidUpdatesOnClassComponentsInDEV(fiber);290  const root = markUpdateTimeFromFiberToRoot(fiber, expirationTime);291  if (root === null) {292    warnAboutUpdateOnUnmountedFiberInDEV(fiber);293    return;294  }295  root.pingTime = NoWork;296  checkForInterruption(fiber, expirationTime);297  recordScheduleUpdate();298  if (expirationTime === Sync) {299    if (workPhase === LegacyUnbatchedPhase) {300      // This is a legacy edge case. The initial mount of a ReactDOM.render-ed301      // root inside of batchedUpdates should be synchronous, but layout updates302      // should be deferred until the end of the batch.303      let callback = renderRoot(root, Sync, true);304      while (callback !== null) {305        callback = callback(true);306      }307    } else {308      scheduleCallbackForRoot(root, ImmediatePriority, Sync);309      if (workPhase === NotWorking) {310        // Flush the synchronous work now, wnless we're already working or inside311        // a batch. This is intentionally inside scheduleUpdateOnFiber instead of312        // scheduleCallbackForFiber to preserve the ability to schedule a callback313        // without immediately flushing it. We only do this for user-initated314        // updates, to preserve historical behavior of sync mode.315        flushImmediateQueue();316      }317    }318  } else {319    // TODO: computeExpirationForFiber also reads the priority. Pass the320    // priority as an argument to that function and this one.321    const priorityLevel = getCurrentPriorityLevel();322    if (priorityLevel === UserBlockingPriority) {323      // This is the result of a discrete event. Track the lowest priority324      // discrete update per root so we can flush them early, if needed.325      if (rootsWithPendingDiscreteUpdates === null) {326        rootsWithPendingDiscreteUpdates = new Map([[root, expirationTime]]);327      } else {328        const lastDiscreteTime = rootsWithPendingDiscreteUpdates.get(root);329        if (330          lastDiscreteTime === undefined ||331          lastDiscreteTime > expirationTime332        ) {333          rootsWithPendingDiscreteUpdates.set(root, expirationTime);334        }335      }336    }337    scheduleCallbackForRoot(root, priorityLevel, expirationTime);338  }339}340export const scheduleWork = scheduleUpdateOnFiber;341// This is split into a separate function so we can mark a fiber with pending342// work without treating it as a typical update that originates from an event;343// e.g. retrying a Suspense boundary isn't an update, but it does schedule work344// on a fiber.345function markUpdateTimeFromFiberToRoot(fiber, expirationTime) {346  // Update the source fiber's expiration time347  if (fiber.expirationTime < expirationTime) {348    fiber.expirationTime = expirationTime;349  }350  let alternate = fiber.alternate;351  if (alternate !== null && alternate.expirationTime < expirationTime) {352    alternate.expirationTime = expirationTime;353  }354  // Walk the parent path to the root and update the child expiration time.355  let node = fiber.return;356  let root = null;357  if (node === null && fiber.tag === HostRoot) {358    root = fiber.stateNode;359  } else {360    while (node !== null) {361      alternate = node.alternate;362      if (node.childExpirationTime < expirationTime) {363        node.childExpirationTime = expirationTime;364        if (365          alternate !== null &&366          alternate.childExpirationTime < expirationTime367        ) {368          alternate.childExpirationTime = expirationTime;369        }370      } else if (371        alternate !== null &&372        alternate.childExpirationTime < expirationTime373      ) {374        alternate.childExpirationTime = expirationTime;375      }376      if (node.return === null && node.tag === HostRoot) {377        root = node.stateNode;378        break;379      }380      node = node.return;381    }382  }383  if (root !== null) {384    // Update the first and last pending expiration times in this root385    const firstPendingTime = root.firstPendingTime;386    if (expirationTime > firstPendingTime) {387      root.firstPendingTime = expirationTime;388    }389    const lastPendingTime = root.lastPendingTime;390    if (lastPendingTime === NoWork || expirationTime < lastPendingTime) {391      root.lastPendingTime = expirationTime;392    }393  }394  return root;395}396// Use this function, along with runRootCallback, to ensure that only a single397// callback per root is scheduled. It's still possible to call renderRoot398// directly, but scheduling via this function helps avoid excessive callbacks.399// It works by storing the callback node and expiration time on the root. When a400// new callback comes in, it compares the expiration time to determine if it401// should cancel the previous one. It also relies on commitRoot scheduling a402// callback to render the next level, because that means we don't need a403// separate callback per expiration time.404function scheduleCallbackForRoot(405  root: FiberRoot,406  priorityLevel: ReactPriorityLevel,407  expirationTime: ExpirationTime,408) {409  const existingCallbackExpirationTime = root.callbackExpirationTime;410  if (existingCallbackExpirationTime < expirationTime) {411    // New callback has higher priority than the existing one.412    const existingCallbackNode = root.callbackNode;413    if (existingCallbackNode !== null) {414      cancelCallback(existingCallbackNode);415    }416    root.callbackExpirationTime = expirationTime;417    let options = null;418    if (expirationTime !== Sync && expirationTime !== Never) {419      let timeout = expirationTimeToMs(expirationTime) - now();420      if (timeout > 5000) {421        // Sanity check. Should never take longer than 5 seconds.422        // TODO: Add internal warning?423        timeout = 5000;424      }425      options = {timeout};426    }427    root.callbackNode = scheduleCallback(428      priorityLevel,429      runRootCallback.bind(430        null,431        root,432        renderRoot.bind(null, root, expirationTime),433      ),434      options,435    );436    if (437      enableUserTimingAPI &&438      expirationTime !== Sync &&439      workPhase !== RenderPhase &&440      workPhase !== CommitPhase441    ) {442      // Scheduled an async callback, and we're not already working. Add an443      // entry to the flamegraph that shows we're waiting for a callback444      // to fire.445      startRequestCallbackTimer();446    }447  }448  // Add the current set of interactions to the pending set associated with449  // this root.450  schedulePendingInteraction(root, expirationTime);451}452function runRootCallback(root, callback, isSync) {453  const prevCallbackNode = root.callbackNode;454  let continuation = null;455  try {456    continuation = callback(isSync);457    if (continuation !== null) {458      return runRootCallback.bind(null, root, continuation);459    } else {460      return null;461    }462  } finally {463    // If the callback exits without returning a continuation, remove the464    // corresponding callback node from the root. Unless the callback node465    // has changed, which implies that it was already cancelled by a high466    // priority update.467    if (continuation === null && prevCallbackNode === root.callbackNode) {468      root.callbackNode = null;469      root.callbackExpirationTime = NoWork;470    }471  }472}473export function flushRoot(root: FiberRoot, expirationTime: ExpirationTime) {474  if (workPhase === RenderPhase || workPhase === CommitPhase) {475    invariant(476      false,477      'work.commit(): Cannot commit while already rendering. This likely ' +478        'means you attempted to commit from inside a lifecycle method.',479    );480  }481  scheduleCallback(482    ImmediatePriority,483    renderRoot.bind(null, root, expirationTime),484  );485  flushImmediateQueue();486}487export function flushInteractiveUpdates() {488  if (workPhase === RenderPhase || workPhase === CommitPhase) {489    // Can't synchronously flush interactive updates if React is already490    // working. This is currently a no-op.491    // TODO: Should we fire a warning? This happens if you synchronously invoke492    // an input event inside an effect, like with `element.click()`.493    return;494  }495  flushPendingDiscreteUpdates();496}497function resolveLocksOnRoot(root: FiberRoot, expirationTime: ExpirationTime) {498  const firstBatch = root.firstBatch;499  if (500    firstBatch !== null &&501    firstBatch._defer &&502    firstBatch._expirationTime >= expirationTime503  ) {504    root.finishedWork = root.current.alternate;505    root.pendingCommitExpirationTime = expirationTime;506    scheduleCallback(NormalPriority, () => {507      firstBatch._onComplete();508      return null;509    });510    return true;511  } else {512    return false;513  }514}515export function deferredUpdates<A>(fn: () => A): A {516  // TODO: Remove in favor of Scheduler.next517  return runWithPriority(NormalPriority, fn);518}519export function interactiveUpdates<A, B, C, R>(520  fn: (A, B, C) => R,521  a: A,522  b: B,523  c: C,524): R {525  if (workPhase === NotWorking) {526    // TODO: Remove this call. Instead of doing this automatically, the caller527    // should explicitly call flushInteractiveUpdates.528    flushPendingDiscreteUpdates();529  }530  return runWithPriority(UserBlockingPriority, fn.bind(null, a, b, c));531}532export function syncUpdates<A, B, C, R>(533  fn: (A, B, C) => R,534  a: A,535  b: B,536  c: C,537): R {538  return runWithPriority(ImmediatePriority, fn.bind(null, a, b, c));539}540function flushPendingDiscreteUpdates() {541  if (rootsWithPendingDiscreteUpdates !== null) {542    // For each root with pending discrete updates, schedule a callback to543    // immediately flush them.544    const roots = rootsWithPendingDiscreteUpdates;545    rootsWithPendingDiscreteUpdates = null;546    roots.forEach((expirationTime, root) => {547      scheduleCallback(548        ImmediatePriority,549        renderRoot.bind(null, root, expirationTime),550      );551    });552    // Now flush the immediate queue.553    flushImmediateQueue();554  }555}556export function batchedUpdates<A, R>(fn: A => R, a: A): R {557  if (workPhase !== NotWorking) {558    // We're already working, or inside a batch, so batchedUpdates is a no-op.559    return fn(a);560  }561  workPhase = BatchedPhase;562  try {563    return fn(a);564  } finally {565    workPhase = NotWorking;566    // Flush the immediate callbacks that were scheduled during this batch567    flushImmediateQueue();568  }569}570export function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {571  if (workPhase !== BatchedPhase && workPhase !== FlushSyncPhase) {572    // We're not inside batchedUpdates or flushSync, so unbatchedUpdates is573    // a no-op.574    return fn(a);575  }576  const prevWorkPhase = workPhase;577  workPhase = LegacyUnbatchedPhase;578  try {579    return fn(a);580  } finally {581    workPhase = prevWorkPhase;582  }583}584export function flushSync<A, R>(fn: A => R, a: A): R {585  if (workPhase === RenderPhase || workPhase === CommitPhase) {586    invariant(587      false,588      'flushSync was called from inside a lifecycle method. It cannot be ' +589        'called when React is already rendering.',590    );591  }592  const prevWorkPhase = workPhase;593  workPhase = FlushSyncPhase;594  try {595    return runWithPriority(ImmediatePriority, fn.bind(null, a));596  } finally {597    workPhase = prevWorkPhase;598    // Flush the immediate callbacks that were scheduled during this batch.599    // Note that this will happen even if batchedUpdates is higher up600    // the stack.601    flushImmediateQueue();602  }603}604export function flushControlled(fn: () => mixed): void {605  const prevWorkPhase = workPhase;606  workPhase = BatchedPhase;607  try {608    runWithPriority(ImmediatePriority, fn);609  } finally {610    workPhase = prevWorkPhase;611    if (workPhase === NotWorking) {612      // Flush the immediate callbacks that were scheduled during this batch613      flushImmediateQueue();614    }615  }616}617function prepareFreshStack(root, expirationTime) {618  root.pendingCommitExpirationTime = NoWork;619  const timeoutHandle = root.timeoutHandle;620  if (timeoutHandle !== noTimeout) {621    // The root previous suspended and scheduled a timeout to commit a fallback622    // state. Now that we have additional work, cancel the timeout.623    root.timeoutHandle = noTimeout;624    // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above625    cancelTimeout(timeoutHandle);626  }627  if (workInProgress !== null) {628    let interruptedWork = workInProgress.return;629    while (interruptedWork !== null) {630      unwindInterruptedWork(interruptedWork);631      interruptedWork = interruptedWork.return;632    }633  }634  workInProgressRoot = root;635  workInProgress = createWorkInProgress(root.current, null, expirationTime);636  renderExpirationTime = expirationTime;637  workInProgressRootExitStatus = RootIncomplete;638  workInProgressRootMostRecentEventTime = Sync;639  if (__DEV__) {640    ReactStrictModeWarnings.discardPendingWarnings();641  }642}643function renderRoot(644  root: FiberRoot,645  expirationTime: ExpirationTime,646  isSync: boolean,647): SchedulerCallback | null {648  invariant(649    workPhase !== RenderPhase && workPhase !== CommitPhase,650    'Should not already be working.',651  );652  if (enableUserTimingAPI && expirationTime !== Sync) {653    const didExpire = isSync;654    stopRequestCallbackTimer(didExpire);655  }656  if (root.firstPendingTime < expirationTime) {657    // If there's no work left at this expiration time, exit immediately. This658    // happens when multiple callbacks are scheduled for a single root, but an659    // earlier callback flushes the work of a later one.660    return null;661  }662  if (root.pendingCommitExpirationTime === expirationTime) {663    // There's already a pending commit at this expiration time.664    root.pendingCommitExpirationTime = NoWork;665    return commitRoot.bind(null, root, expirationTime);666  }667  flushPassiveEffects();668  // If the root or expiration time have changed, throw out the existing stack669  // and prepare a fresh one. Otherwise we'll continue where we left off.670  if (root !== workInProgressRoot || expirationTime !== renderExpirationTime) {671    prepareFreshStack(root, expirationTime);672    startWorkOnPendingInteraction(root, expirationTime);673  }674  // If we have a work-in-progress fiber, it means there's still work to do675  // in this root.676  if (workInProgress !== null) {677    const prevWorkPhase = workPhase;678    workPhase = RenderPhase;679    let prevDispatcher = ReactCurrentDispatcher.current;680    if (prevDispatcher === null) {681      // The React isomorphic package does not include a default dispatcher.682      // Instead the first renderer will lazily attach one, in order to give683      // nicer error messages.684      prevDispatcher = ContextOnlyDispatcher;685    }686    ReactCurrentDispatcher.current = ContextOnlyDispatcher;687    let prevInteractions: Set<Interaction> | null = null;688    if (enableSchedulerTracing) {689      prevInteractions = __interactionsRef.current;690      __interactionsRef.current = root.memoizedInteractions;691    }692    startWorkLoopTimer(workInProgress);693    // TODO: Fork renderRoot into renderRootSync and renderRootAsync694    if (isSync) {695      if (expirationTime !== Sync) {696        // An async update expired. There may be other expired updates on697        // this root. We should render all the expired work in a698        // single batch.699        const currentTime = requestCurrentTime();700        if (currentTime < expirationTime) {701          // Restart at the current time.702          workPhase = prevWorkPhase;703          resetContextDependencies();704          ReactCurrentDispatcher.current = prevDispatcher;705          if (enableSchedulerTracing) {706            __interactionsRef.current = ((prevInteractions: any): Set<707              Interaction,708            >);709          }710          return renderRoot.bind(null, root, currentTime);711        }712      }713    } else {714      // Since we know we're in a React event, we can clear the current715      // event time. The next update will compute a new event time.716      currentEventTime = NoWork;717    }718    do {719      try {720        if (isSync) {721          workLoopSync();722        } else {723          workLoop();724        }725        break;726      } catch (thrownValue) {727        // Reset module-level state that was set during the render phase.728        resetContextDependencies();729        resetHooks();730        const sourceFiber = workInProgress;731        if (sourceFiber === null || sourceFiber.return === null) {732          // Expected to be working on a non-root fiber. This is a fatal error733          // because there's no ancestor that can handle it; the root is734          // supposed to capture all errors that weren't caught by an error735          // boundary.736          prepareFreshStack(root, expirationTime);737          workPhase = prevWorkPhase;738          throw thrownValue;739        }740        if (enableProfilerTimer && sourceFiber.mode & ProfileMode) {741          // Record the time spent rendering before an error was thrown. This742          // avoids inaccurate Profiler durations in the case of a743          // suspended render.744          stopProfilerTimerIfRunningAndRecordDelta(sourceFiber, true);745        }746        const returnFiber = sourceFiber.return;747        throwException(748          root,749          returnFiber,750          sourceFiber,751          thrownValue,752          renderExpirationTime,753        );754        workInProgress = completeUnitOfWork(sourceFiber);755      }756    } while (true);757    workPhase = prevWorkPhase;758    resetContextDependencies();759    ReactCurrentDispatcher.current = prevDispatcher;760    if (enableSchedulerTracing) {761      __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);762    }763    if (workInProgress !== null) {764      // There's still work left over. Return a continuation.765      stopInterruptedWorkLoopTimer();766      if (expirationTime !== Sync) {767        startRequestCallbackTimer();768      }769      return renderRoot.bind(null, root, expirationTime);770    }771  }772  // We now have a consistent tree. The next step is either to commit it, or, if773  // something suspended, wait to commit it after a timeout.774  stopFinishedWorkLoopTimer();775  const isLocked = resolveLocksOnRoot(root, expirationTime);776  if (isLocked) {777    // This root has a lock that prevents it from committing. Exit. If we begin778    // work on the root again, without any intervening updates, it will finish779    // without doing additional work.780    return null;781  }782  // Set this to null to indicate there's no in-progress render.783  workInProgressRoot = null;784  switch (workInProgressRootExitStatus) {785    case RootIncomplete: {786      invariant(false, 'Should have a work-in-progress.');787    }788    // Flow knows about invariant, so it compains if I add a break statement,789    // but eslint doesn't know about invariant, so it complains if I do.790    // eslint-disable-next-line no-fallthrough791    case RootErrored: {792      // An error was thrown. First check if there is lower priority work793      // scheduled on this root.794      const lastPendingTime = root.lastPendingTime;795      if (root.lastPendingTime < expirationTime) {796        // There's lower priority work. Before raising the error, try rendering797        // at the lower priority to see if it fixes it. Use a continuation to798        // maintain the existing priority and position in the queue.799        return renderRoot.bind(null, root, lastPendingTime);800      }801      if (!isSync) {802        // If we're rendering asynchronously, it's possible the error was803        // caused by tearing due to a mutation during an event. Try rendering804        // one more time without yiedling to events.805        prepareFreshStack(root, expirationTime);806        scheduleCallback(807          ImmediatePriority,808          renderRoot.bind(null, root, expirationTime),809        );810        return null;811      }812      // If we're already rendering synchronously, commit the root in its813      // errored state.814      return commitRoot.bind(null, root, expirationTime);815    }816    case RootSuspended: {817      if (!isSync) {818        const lastPendingTime = root.lastPendingTime;819        if (root.lastPendingTime < expirationTime) {820          // There's lower priority work. It might be unsuspended. Try rendering821          // at that level.822          return renderRoot.bind(null, root, lastPendingTime);823        }824        // If workInProgressRootMostRecentEventTime is Sync, that means we didn't825        // track any event times. That can happen if we retried but nothing switched826        // from fallback to content. There's no reason to delay doing no work.827        if (workInProgressRootMostRecentEventTime !== Sync) {828          let msUntilTimeout = computeMsUntilTimeout(829            workInProgressRootMostRecentEventTime,830            expirationTime,831          );832          // Don't bother with a very short suspense time.833          if (msUntilTimeout > 10) {834            // The render is suspended, it hasn't timed out, and there's no lower835            // priority work to do. Instead of committing the fallback836            // immediately, wait for more data to arrive.837            root.timeoutHandle = scheduleTimeout(838              commitRoot.bind(null, root, expirationTime),839              msUntilTimeout,840            );841            return null;842          }843        }844      }845      // The work expired. Commit immediately.846      return commitRoot.bind(null, root, expirationTime);847    }848    case RootCompleted: {849      // The work completed. Ready to commit.850      return commitRoot.bind(null, root, expirationTime);851    }852    default: {853      invariant(false, 'Unknown root exit status.');854    }855  }856}857export function markRenderEventTime(expirationTime: ExpirationTime): void {858  if (expirationTime < workInProgressRootMostRecentEventTime) {859    workInProgressRootMostRecentEventTime = expirationTime;860  }861}862export function renderDidSuspend(): void {863  if (workInProgressRootExitStatus === RootIncomplete) {864    workInProgressRootExitStatus = RootSuspended;865  }866}867export function renderDidError() {868  if (869    workInProgressRootExitStatus === RootIncomplete ||870    workInProgressRootExitStatus === RootSuspended871  ) {872    workInProgressRootExitStatus = RootErrored;873  }874}875function inferTimeFromExpirationTime(expirationTime: ExpirationTime): number {876  // We don't know exactly when the update was scheduled, but we can infer an877  // approximate start time from the expiration time.878  const earliestExpirationTimeMs = expirationTimeToMs(expirationTime);879  return earliestExpirationTimeMs - LOW_PRIORITY_EXPIRATION;880}881function workLoopSync() {882  // Already timed out, so perform work without checking if we need to yield.883  while (workInProgress !== null) {884    workInProgress = performUnitOfWork(workInProgress);885  }886}887function workLoop() {888  // Perform work until Scheduler asks us to yield889  while (workInProgress !== null && !shouldYield()) {890    workInProgress = performUnitOfWork(workInProgress);891  }892}893function performUnitOfWork(unitOfWork: Fiber): Fiber | null {894  // The current, flushed, state of this fiber is the alternate. Ideally895  // nothing should rely on this, but relying on it here means that we don't896  // need an additional field on the work in progress.897  const current = unitOfWork.alternate;898  startWorkTimer(unitOfWork);899  setCurrentDebugFiberInDEV(unitOfWork);900  let next;901  if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoContext) {902    startProfilerTimer(unitOfWork);903    next = beginWork(current, unitOfWork, renderExpirationTime);904    stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);905  } else {906    next = beginWork(current, unitOfWork, renderExpirationTime);907  }908  resetCurrentDebugFiberInDEV();909  unitOfWork.memoizedProps = unitOfWork.pendingProps;910  if (next === null) {911    // If this doesn't spawn new work, complete the current work.912    next = completeUnitOfWork(unitOfWork);913  }914  ReactCurrentOwner.current = null;915  return next;916}917function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {918  // Attempt to complete the current unit of work, then move to the next919  // sibling. If there are no more siblings, return to the parent fiber.920  workInProgress = unitOfWork;921  do {922    // The current, flushed, state of this fiber is the alternate. Ideally923    // nothing should rely on this, but relying on it here means that we don't924    // need an additional field on the work in progress.925    const current = workInProgress.alternate;926    const returnFiber = workInProgress.return;927    // Check if the work completed or if something threw.928    if ((workInProgress.effectTag & Incomplete) === NoEffect) {929      setCurrentDebugFiberInDEV(workInProgress);930      let next;931      if (932        !enableProfilerTimer ||933        (workInProgress.mode & ProfileMode) === NoContext934      ) {935        next = completeWork(current, workInProgress, renderExpirationTime);936      } else {937        startProfilerTimer(workInProgress);938        next = completeWork(current, workInProgress, renderExpirationTime);939        // Update render duration assuming we didn't error.940        stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);941      }942      stopWorkTimer(workInProgress);943      resetCurrentDebugFiberInDEV();944      resetChildExpirationTime(workInProgress);945      if (next !== null) {946        // Completing this fiber spawned new work. Work on that next.947        return next;948      }949      if (950        returnFiber !== null &&951        // Do not append effects to parents if a sibling failed to complete952        (returnFiber.effectTag & Incomplete) === NoEffect953      ) {954        // Append all the effects of the subtree and this fiber onto the effect955        // list of the parent. The completion order of the children affects the956        // side-effect order.957        if (returnFiber.firstEffect === null) {958          returnFiber.firstEffect = workInProgress.firstEffect;959        }960        if (workInProgress.lastEffect !== null) {961          if (returnFiber.lastEffect !== null) {962            returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;963          }964          returnFiber.lastEffect = workInProgress.lastEffect;965        }966        // If this fiber had side-effects, we append it AFTER the children's967        // side-effects. We can perform certain side-effects earlier if needed,968        // by doing multiple passes over the effect list. We don't want to969        // schedule our own side-effect on our own list because if end up970        // reusing children we'll schedule this effect onto itself since we're971        // at the end.972        const effectTag = workInProgress.effectTag;973        // Skip both NoWork and PerformedWork tags when creating the effect974        // list. PerformedWork effect is read by React DevTools but shouldn't be975        // committed.976        if (effectTag > PerformedWork) {977          if (returnFiber.lastEffect !== null) {978            returnFiber.lastEffect.nextEffect = workInProgress;979          } else {980            returnFiber.firstEffect = workInProgress;981          }982          returnFiber.lastEffect = workInProgress;983        }984      }985    } else {986      // This fiber did not complete because something threw. Pop values off987      // the stack without entering the complete phase. If this is a boundary,988      // capture values if possible.989      const next = unwindWork(workInProgress, renderExpirationTime);990      // Because this fiber did not complete, don't reset its expiration time.991      if (992        enableProfilerTimer &&993        (workInProgress.mode & ProfileMode) !== NoContext994      ) {995        // Record the render duration for the fiber that errored.996        stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);997        // Include the time spent working on failed children before continuing.998        let actualDuration = workInProgress.actualDuration;999        let child = workInProgress.child;1000        while (child !== null) {1001          actualDuration += child.actualDuration;1002          child = child.sibling;1003        }1004        workInProgress.actualDuration = actualDuration;1005      }1006      if (next !== null) {1007        // If completing this work spawned new work, do that next. We'll come1008        // back here again.1009        // Since we're restarting, remove anything that is not a host effect1010        // from the effect tag.1011        // TODO: The name stopFailedWorkTimer is misleading because Suspense1012        // also captures and restarts.1013        stopFailedWorkTimer(workInProgress);1014        next.effectTag &= HostEffectMask;1015        return next;1016      }1017      stopWorkTimer(workInProgress);1018      if (returnFiber !== null) {1019        // Mark the parent fiber as incomplete and clear its effect list.1020        returnFiber.firstEffect = returnFiber.lastEffect = null;1021        returnFiber.effectTag |= Incomplete;1022      }1023    }1024    const siblingFiber = workInProgress.sibling;1025    if (siblingFiber !== null) {1026      // If there is more work to do in this returnFiber, do that next.1027      return siblingFiber;1028    }1029    // Otherwise, return to the parent1030    workInProgress = returnFiber;1031  } while (workInProgress !== null);1032  // We've reached the root.1033  if (workInProgressRootExitStatus === RootIncomplete) {1034    workInProgressRootExitStatus = RootCompleted;1035  }1036  return null;1037}1038function resetChildExpirationTime(completedWork: Fiber) {1039  if (1040    renderExpirationTime !== Never &&1041    completedWork.childExpirationTime === Never1042  ) {1043    // The children of this component are hidden. Don't bubble their1044    // expiration times.1045    return;1046  }1047  let newChildExpirationTime = NoWork;1048  // Bubble up the earliest expiration time.1049  if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoContext) {1050    // In profiling mode, resetChildExpirationTime is also used to reset1051    // profiler durations.1052    let actualDuration = completedWork.actualDuration;1053    let treeBaseDuration = completedWork.selfBaseDuration;1054    // When a fiber is cloned, its actualDuration is reset to 0. This value will1055    // only be updated if work is done on the fiber (i.e. it doesn't bailout).1056    // When work is done, it should bubble to the parent's actualDuration. If1057    // the fiber has not been cloned though, (meaning no work was done), then1058    // this value will reflect the amount of time spent working on a previous1059    // render. In that case it should not bubble. We determine whether it was1060    // cloned by comparing the child pointer.1061    const shouldBubbleActualDurations =1062      completedWork.alternate === null ||1063      completedWork.child !== completedWork.alternate.child;1064    let child = completedWork.child;1065    while (child !== null) {1066      const childUpdateExpirationTime = child.expirationTime;1067      const childChildExpirationTime = child.childExpirationTime;1068      if (childUpdateExpirationTime > newChildExpirationTime) {1069        newChildExpirationTime = childUpdateExpirationTime;1070      }1071      if (childChildExpirationTime > newChildExpirationTime) {1072        newChildExpirationTime = childChildExpirationTime;1073      }1074      if (shouldBubbleActualDurations) {1075        actualDuration += child.actualDuration;1076      }1077      treeBaseDuration += child.treeBaseDuration;1078      child = child.sibling;1079    }1080    completedWork.actualDuration = actualDuration;1081    completedWork.treeBaseDuration = treeBaseDuration;1082  } else {1083    let child = completedWork.child;1084    while (child !== null) {1085      const childUpdateExpirationTime = child.expirationTime;1086      const childChildExpirationTime = child.childExpirationTime;1087      if (childUpdateExpirationTime > newChildExpirationTime) {1088        newChildExpirationTime = childUpdateExpirationTime;1089      }1090      if (childChildExpirationTime > newChildExpirationTime) {1091        newChildExpirationTime = childChildExpirationTime;1092      }1093      child = child.sibling;1094    }1095  }1096  completedWork.childExpirationTime = newChildExpirationTime;1097}1098function commitRoot(root, expirationTime) {1099  runWithPriority(1100    ImmediatePriority,1101    commitRootImpl.bind(null, root, expirationTime),1102  );1103  // If there are passive effects, schedule a callback to flush them. This goes1104  // outside commitRootImpl so that it inherits the priority of the render.1105  if (rootWithPendingPassiveEffects !== null) {1106    const priorityLevel = getCurrentPriorityLevel();1107    scheduleCallback(priorityLevel, () => {1108      flushPassiveEffects();1109      return null;1110    });1111  }1112  return null;1113}1114function commitRootImpl(root, expirationTime) {1115  flushPassiveEffects();1116  flushRenderPhaseStrictModeWarningsInDEV();1117  invariant(1118    workPhase !== RenderPhase && workPhase !== CommitPhase,1119    'Should not already be working.',1120  );1121  const finishedWork = root.current.alternate;1122  invariant(finishedWork !== null, 'Should have a work-in-progress root.');1123  // commitRoot never returns a continuation; it always finishes synchronously.1124  // So we can clear these now to allow a new callback to be scheduled.1125  root.callbackNode = null;1126  root.callbackExpirationTime = NoWork;1127  startCommitTimer();1128  // Update the first and last pending times on this root. The new first1129  // pending time is whatever is left on the root fiber.1130  const updateExpirationTimeBeforeCommit = finishedWork.expirationTime;1131  const childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;1132  const firstPendingTimeBeforeCommit =1133    childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit1134      ? childExpirationTimeBeforeCommit1135      : updateExpirationTimeBeforeCommit;1136  root.firstPendingTime = firstPendingTimeBeforeCommit;1137  if (firstPendingTimeBeforeCommit < root.lastPendingTime) {1138    // This usually means we've finished all the work, but it can also happen1139    // when something gets downprioritized during render, like a hidden tree.1140    root.lastPendingTime = firstPendingTimeBeforeCommit;1141  }1142  if (root === workInProgressRoot) {1143    // We can reset these now that they are finished.1144    workInProgressRoot = null;1145    workInProgress = null;1146    renderExpirationTime = NoWork;1147  } else {1148    // This indicates that the last root we worked on is not the same one that1149    // we're committing now. This most commonly happens when a suspended root1150    // times out.1151  }1152  // Get the list of effects.1153  let firstEffect;1154  if (finishedWork.effectTag > PerformedWork) {1155    // A fiber's effect list consists only of its children, not itself. So if1156    // the root has an effect, we need to add it to the end of the list. The1157    // resulting list is the set that would belong to the root's parent, if it1158    // had one; that is, all the effects in the tree including the root.1159    if (finishedWork.lastEffect !== null) {1160      finishedWork.lastEffect.nextEffect = finishedWork;1161      firstEffect = finishedWork.firstEffect;1162    } else {1163      firstEffect = finishedWork;1164    }1165  } else {1166    // There is no effect on the root.1167    firstEffect = finishedWork.firstEffect;1168  }1169  if (firstEffect !== null) {1170    const prevWorkPhase = workPhase;1171    workPhase = CommitPhase;1172    let prevInteractions: Set<Interaction> | null = null;1173    if (enableSchedulerTracing) {1174      prevInteractions = __interactionsRef.current;1175      __interactionsRef.current = root.memoizedInteractions;1176    }1177    // Reset this to null before calling lifecycles1178    ReactCurrentOwner.current = null;1179    // The commit phase is broken into several sub-phases. We do a separate pass1180    // of the effect list for each phase: all mutation effects come before all1181    // layout effects, and so on.1182    // The first phase a "before mutation" phase. We use this phase to read the1183    // state of the host tree right before we mutate it. This is where1184    // getSnapshotBeforeUpdate is called.1185    startCommitSnapshotEffectsTimer();1186    prepareForCommit(root.containerInfo);1187    nextEffect = firstEffect;1188    do {1189      if (__DEV__) {1190        invokeGuardedCallback(null, commitBeforeMutationEffects, null);1191        if (hasCaughtError()) {1192          invariant(nextEffect !== null, 'Should be working on an effect.');1193          const error = clearCaughtError();1194          captureCommitPhaseError(nextEffect, error);1195          nextEffect = nextEffect.nextEffect;1196        }1197      } else {1198        try {1199          commitBeforeMutationEffects();1200        } catch (error) {1201          invariant(nextEffect !== null, 'Should be working on an effect.');1202          captureCommitPhaseError(nextEffect, error);1203          nextEffect = nextEffect.nextEffect;1204        }1205      }1206    } while (nextEffect !== null);1207    stopCommitSnapshotEffectsTimer();1208    if (enableProfilerTimer) {1209      // Mark the current commit time to be shared by all Profilers in this1210      // batch. This enables them to be grouped later.1211      recordCommitTime();1212    }1213    // The next phase is the mutation phase, where we mutate the host tree.1214    startCommitHostEffectsTimer();1215    nextEffect = firstEffect;1216    do {1217      if (__DEV__) {1218        invokeGuardedCallback(null, commitMutationEffects, null);1219        if (hasCaughtError()) {1220          invariant(nextEffect !== null, 'Should be working on an effect.');1221          const error = clearCaughtError();1222          captureCommitPhaseError(nextEffect, error);1223          nextEffect = nextEffect.nextEffect;1224        }1225      } else {1226        try {1227          commitMutationEffects();1228        } catch (error) {1229          invariant(nextEffect !== null, 'Should be working on an effect.');1230          captureCommitPhaseError(nextEffect, error);1231          nextEffect = nextEffect.nextEffect;1232        }1233      }1234    } while (nextEffect !== null);1235    stopCommitHostEffectsTimer();1236    resetAfterCommit(root.containerInfo);1237    // The work-in-progress tree is now the current tree. This must come after1238    // the mutation phase, so that the previous tree is still current during1239    // componentWillUnmount, but before the layout phase, so that the finished1240    // work is current during componentDidMount/Update.1241    root.current = finishedWork;1242    // The next phase is the layout phase, where we call effects that read1243    // the host tree after it's been mutated. The idiomatic use case for this is1244    // layout, but class component lifecycles also fire here for legacy reasons.1245    startCommitLifeCyclesTimer();1246    nextEffect = firstEffect;1247    do {1248      if (__DEV__) {1249        invokeGuardedCallback(1250          null,1251          commitLayoutEffects,1252          null,1253          root,1254          expirationTime,1255        );1256        if (hasCaughtError()) {1257          invariant(nextEffect !== null, 'Should be working on an effect.');1258          const error = clearCaughtError();1259          captureCommitPhaseError(nextEffect, error);1260          nextEffect = nextEffect.nextEffect;1261        }1262      } else {1263        try {1264          commitLayoutEffects(root, expirationTime);1265        } catch (error) {1266          invariant(nextEffect !== null, 'Should be working on an effect.');1267          captureCommitPhaseError(nextEffect, error);1268          nextEffect = nextEffect.nextEffect;1269        }1270      }1271    } while (nextEffect !== null);1272    stopCommitLifeCyclesTimer();1273    nextEffect = null;1274    if (enableSchedulerTracing) {1275      __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1276    }1277    workPhase = prevWorkPhase;1278  } else {1279    // No effects.1280    root.current = finishedWork;1281    // Measure these anyway so the flamegraph explicitly shows that there were1282    // no effects.1283    // TODO: Maybe there's a better way to report this.1284    startCommitSnapshotEffectsTimer();1285    stopCommitSnapshotEffectsTimer();1286    if (enableProfilerTimer) {1287      recordCommitTime();1288    }1289    startCommitHostEffectsTimer();1290    stopCommitHostEffectsTimer();1291    startCommitLifeCyclesTimer();1292    stopCommitLifeCyclesTimer();1293  }1294  stopCommitTimer();1295  if (rootDoesHavePassiveEffects) {1296    // This commit has passive effects. Stash a reference to them. But don't1297    // schedule a callback until after flushing layout work.1298    rootDoesHavePassiveEffects = false;1299    rootWithPendingPassiveEffects = root;1300    pendingPassiveEffectsExpirationTime = expirationTime;1301  } else {1302    if (enableSchedulerTracing) {1303      // If there are no passive effects, then we can complete the pending1304      // interactions. Otherwise, we'll wait until after the passive effects1305      // are flushed.1306      finishPendingInteractions(root, expirationTime);1307    }1308  }1309  // Check if there's remaining work on this root1310  const remainingExpirationTime = root.firstPendingTime;1311  if (remainingExpirationTime !== NoWork) {1312    const currentTime = requestCurrentTime();1313    const priorityLevel = inferPriorityFromExpirationTime(1314      currentTime,1315      remainingExpirationTime,1316    );1317    scheduleCallbackForRoot(root, priorityLevel, remainingExpirationTime);1318  } else {1319    // If there's no remaining work, we can clear the set of already failed1320    // error boundaries.1321    legacyErrorBoundariesThatAlreadyFailed = null;1322  }1323  onCommitRoot(finishedWork.stateNode);1324  if (remainingExpirationTime === Sync) {1325    // Count the number of times the root synchronously re-renders without1326    // finishing. If there are too many, it indicates an infinite update loop.1327    if (root === rootWithNestedUpdates) {1328      nestedUpdateCount++;1329    } else {1330      nestedUpdateCount = 0;1331      rootWithNestedUpdates = root;1332    }1333  } else {1334    nestedUpdateCount = 0;1335  }1336  if (hasUncaughtError) {1337    hasUncaughtError = false;1338    const error = firstUncaughtError;1339    firstUncaughtError = null;1340    throw error;1341  }1342  if (workPhase === LegacyUnbatchedPhase) {1343    // This is a legacy edge case. We just committed the initial mount of1344    // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1345    // synchronously, but layout updates should be deferred until the end1346    // of the batch.1347    return null;1348  }1349  // If layout work was scheduled, flush it now.1350  flushImmediateQueue();1351  return null;1352}1353function commitBeforeMutationEffects() {1354  while (nextEffect !== null) {1355    if ((nextEffect.effectTag & Snapshot) !== NoEffect) {1356      setCurrentDebugFiberInDEV(nextEffect);1357      recordEffect();1358      const current = nextEffect.alternate;1359      commitBeforeMutationEffectOnFiber(current, nextEffect);1360      resetCurrentDebugFiberInDEV();1361    }1362    nextEffect = nextEffect.nextEffect;1363  }1364}1365function commitMutationEffects() {1366  // TODO: Should probably move the bulk of this function to commitWork.1367  while (nextEffect !== null) {1368    setCurrentDebugFiberInDEV(nextEffect);1369    const effectTag = nextEffect.effectTag;1370    if (effectTag & ContentReset) {1371      commitResetTextContent(nextEffect);1372    }1373    if (effectTag & Ref) {1374      const current = nextEffect.alternate;1375      if (current !== null) {1376        commitDetachRef(current);1377      }1378    }1379    // The following switch statement is only concerned about placement,1380    // updates, and deletions. To avoid needing to add a case for every possible1381    // bitmap value, we remove the secondary effects from the effect tag and1382    // switch on that value.1383    let primaryEffectTag = effectTag & (Placement | Update | Deletion);1384    switch (primaryEffectTag) {1385      case Placement: {1386        commitPlacement(nextEffect);1387        // Clear the "placement" from effect tag so that we know that this is1388        // inserted, before any life-cycles like componentDidMount gets called.1389        // TODO: findDOMNode doesn't rely on this any more but isMounted does1390        // and isMounted is deprecated anyway so we should be able to kill this.1391        nextEffect.effectTag &= ~Placement;1392        break;1393      }1394      case PlacementAndUpdate: {1395        // Placement1396        commitPlacement(nextEffect);1397        // Clear the "placement" from effect tag so that we know that this is1398        // inserted, before any life-cycles like componentDidMount gets called.1399        nextEffect.effectTag &= ~Placement;1400        // Update1401        const current = nextEffect.alternate;1402        commitWork(current, nextEffect);1403        break;1404      }1405      case Update: {1406        const current = nextEffect.alternate;1407        commitWork(current, nextEffect);1408        break;1409      }1410      case Deletion: {1411        commitDeletion(nextEffect);1412        break;1413      }1414    }1415    // TODO: Only record a mutation effect if primaryEffectTag is non-zero.1416    recordEffect();1417    resetCurrentDebugFiberInDEV();1418    nextEffect = nextEffect.nextEffect;1419  }1420}1421function commitLayoutEffects(1422  root: FiberRoot,1423  committedExpirationTime: ExpirationTime,1424) {1425  // TODO: Should probably move the bulk of this function to commitWork.1426  while (nextEffect !== null) {1427    setCurrentDebugFiberInDEV(nextEffect);1428    const effectTag = nextEffect.effectTag;1429    if (effectTag & (Update | Callback)) {1430      recordEffect();1431      const current = nextEffect.alternate;1432      commitLayoutEffectOnFiber(1433        root,1434        current,1435        nextEffect,1436        committedExpirationTime,1437      );1438    }1439    if (effectTag & Ref) {1440      recordEffect();1441      commitAttachRef(nextEffect);1442    }1443    if (effectTag & Passive) {1444      rootDoesHavePassiveEffects = true;1445    }1446    resetCurrentDebugFiberInDEV();1447    nextEffect = nextEffect.nextEffect;1448  }1449}1450export function flushPassiveEffects() {1451  if (rootWithPendingPassiveEffects === null) {1452    return false;1453  }1454  const root = rootWithPendingPassiveEffects;1455  const expirationTime = pendingPassiveEffectsExpirationTime;1456  rootWithPendingPassiveEffects = null;1457  pendingPassiveEffectsExpirationTime = NoWork;1458  let prevInteractions: Set<Interaction> | null = null;1459  if (enableSchedulerTracing) {1460    prevInteractions = __interactionsRef.current;1461    __interactionsRef.current = root.memoizedInteractions;1462  }1463  invariant(1464    workPhase !== RenderPhase && workPhase !== CommitPhase,1465    'Cannot flush passive effects while already rendering.',1466  );1467  const prevWorkPhase = workPhase;1468  workPhase = CommitPhase;1469  // Note: This currently assumes there are no passive effects on the root1470  // fiber, because the root is not part of its own effect list. This could1471  // change in the future.1472  let effect = root.current.firstEffect;1473  while (effect !== null) {1474    if (__DEV__) {1475      setCurrentDebugFiberInDEV(effect);1476      invokeGuardedCallback(null, commitPassiveHookEffects, null, effect);1477      if (hasCaughtError()) {1478        invariant(effect !== null, 'Should be working on an effect.');1479        const error = clearCaughtError();1480        captureCommitPhaseError(effect, error);1481      }1482      resetCurrentDebugFiberInDEV();1483    } else {1484      try {1485        commitPassiveHookEffects(effect);1486      } catch (error) {1487        invariant(effect !== null, 'Should be working on an effect.');1488        captureCommitPhaseError(effect, error);1489      }1490    }1491    effect = effect.nextEffect;1492  }1493  if (enableSchedulerTracing) {1494    __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1495    finishPendingInteractions(root, expirationTime);1496  }1497  workPhase = prevWorkPhase;1498  flushImmediateQueue();1499  // If additional passive effects were scheduled, increment a counter. If this1500  // exceeds the limit, we'll fire a warning.1501  nestedPassiveUpdateCount =1502    rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1503  return true;1504}1505export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {1506  return (1507    legacyErrorBoundariesThatAlreadyFailed !== null &&1508    legacyErrorBoundariesThatAlreadyFailed.has(instance)1509  );1510}1511export function markLegacyErrorBoundaryAsFailed(instance: mixed) {1512  if (legacyErrorBoundariesThatAlreadyFailed === null) {1513    legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1514  } else {1515    legacyErrorBoundariesThatAlreadyFailed.add(instance);1516  }1517}1518function prepareToThrowUncaughtError(error: mixed) {1519  if (!hasUncaughtError) {1520    hasUncaughtError = true;1521    firstUncaughtError = error;1522  }1523}1524export const onUncaughtError = prepareToThrowUncaughtError;1525function captureCommitPhaseErrorOnRoot(1526  rootFiber: Fiber,1527  sourceFiber: Fiber,1528  error: mixed,1529) {1530  const errorInfo = createCapturedValue(error, sourceFiber);1531  const update = createRootErrorUpdate(rootFiber, errorInfo, Sync);1532  enqueueUpdate(rootFiber, update);1533  const root = markUpdateTimeFromFiberToRoot(rootFiber, Sync);1534  if (root !== null) {1535    scheduleCallbackForRoot(root, ImmediatePriority, Sync);1536  }1537}1538export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {1539  if (sourceFiber.tag === HostRoot) {1540    // Error was thrown at the root. There is no parent, so the root1541    // itself should capture it.1542    captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1543    return;1544  }1545  let fiber = sourceFiber.return;1546  while (fiber !== null) {1547    if (fiber.tag === HostRoot) {1548      captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1549      return;1550    } else if (fiber.tag === ClassComponent) {1551      const ctor = fiber.type;1552      const instance = fiber.stateNode;1553      if (1554        typeof ctor.getDerivedStateFromError === 'function' ||1555        (typeof instance.componentDidCatch === 'function' &&1556          !isAlreadyFailedLegacyErrorBoundary(instance))1557      ) {1558        const errorInfo = createCapturedValue(error, sourceFiber);1559        const update = createClassErrorUpdate(1560          fiber,1561          errorInfo,1562          // TODO: This is always sync1563          Sync,1564        );1565        enqueueUpdate(fiber, update);1566        const root = markUpdateTimeFromFiberToRoot(fiber, Sync);1567        if (root !== null) {1568          scheduleCallbackForRoot(root, ImmediatePriority, Sync);1569        }1570        return;1571      }1572    }1573    fiber = fiber.return;1574  }1575}1576export function pingSuspendedRoot(1577  root: FiberRoot,1578  thenable: Thenable,1579  suspendedTime: ExpirationTime,1580) {1581  const pingCache = root.pingCache;1582  if (pingCache !== null) {1583    // The thenable resolved, so we no longer need to memoize, because it will1584    // never be thrown again.1585    pingCache.delete(thenable);1586  }1587  if (workInProgressRoot === root && renderExpirationTime === suspendedTime) {1588    // Received a ping at the same priority level at which we're currently1589    // rendering. Restart from the root. Don't need to schedule a ping because1590    // we're already working on this tree.1591    prepareFreshStack(root, renderExpirationTime);1592    return;1593  }1594  const lastPendingTime = root.lastPendingTime;1595  if (lastPendingTime < suspendedTime) {1596    // The root is no longer suspended at this time.1597    return;1598  }1599  const pingTime = root.pingTime;1600  if (pingTime !== NoWork && pingTime < suspendedTime) {1601    // There's already a lower priority ping scheduled.1602    return;1603  }1604  // Mark the time at which this ping was scheduled.1605  root.pingTime = suspendedTime;1606  const currentTime = requestCurrentTime();1607  const priorityLevel = inferPriorityFromExpirationTime(1608    currentTime,1609    suspendedTime,1610  );1611  scheduleCallbackForRoot(root, priorityLevel, suspendedTime);1612}1613export function retryTimedOutBoundary(boundaryFiber: Fiber) {1614  // The boundary fiber (a Suspense component) previously timed out and was1615  // rendered in its fallback state. One of the promises that suspended it has1616  // resolved, which means at least part of the tree was likely unblocked. Try1617  // rendering again, at a new expiration time.1618  const currentTime = requestCurrentTime();1619  const retryTime = computeExpirationForFiber(currentTime, boundaryFiber);1620  // TODO: Special case idle priority?1621  const priorityLevel = inferPriorityFromExpirationTime(currentTime, retryTime);1622  const root = markUpdateTimeFromFiberToRoot(boundaryFiber, retryTime);1623  if (root !== null) {1624    scheduleCallbackForRoot(root, priorityLevel, retryTime);1625  }1626}1627export function resolveRetryThenable(boundaryFiber: Fiber, thenable: Thenable) {1628  let retryCache: WeakSet<Thenable> | Set<Thenable> | null;1629  if (enableSuspenseServerRenderer) {1630    switch (boundaryFiber.tag) {1631      case SuspenseComponent:1632        retryCache = boundaryFiber.stateNode;1633        break;1634      case DehydratedSuspenseComponent:1635        retryCache = boundaryFiber.memoizedState;1636        break;1637      default:1638        invariant(1639          false,1640          'Pinged unknown suspense boundary type. ' +1641            'This is probably a bug in React.',1642        );1643    }1644  } else {1645    retryCache = boundaryFiber.stateNode;1646  }1647  if (retryCache !== null) {1648    // The thenable resolved, so we no longer need to memoize, because it will1649    // never be thrown again.1650    retryCache.delete(thenable);1651  }1652  retryTimedOutBoundary(boundaryFiber);1653}1654// Computes the next Just Noticeable Difference (JND) boundary.1655// The theory is that a person can't tell the difference between small differences in time.1656// Therefore, if we wait a bit longer than necessary that won't translate to a noticeable1657// difference in the experience. However, waiting for longer might mean that we can avoid1658// showing an intermediate loading state. The longer we have already waited, the harder it1659// is to tell small differences in time. Therefore, the longer we've already waited,1660// the longer we can wait additionally. At some point we have to give up though.1661// We pick a train model where the next boundary commits at a consistent schedule.1662// These particular numbers are vague estimates. We expect to adjust them based on research.1663function jnd(timeElapsed: number) {1664  return timeElapsed < 1201665    ? 1201666    : timeElapsed < 4801667      ? 4801668      : timeElapsed < 10801669        ? 10801670        : timeElapsed < 19201671          ? 19201672          : timeElapsed < 30001673            ? 30001674            : timeElapsed < 43201675              ? 43201676              : ceil(timeElapsed / 1960) * 1960;1677}1678function computeMsUntilTimeout(1679  mostRecentEventTime: ExpirationTime,1680  committedExpirationTime: ExpirationTime,1681) {1682  if (disableYielding) {1683    // Timeout immediately when yielding is disabled.1684    return 0;1685  }1686  const eventTimeMs: number = inferTimeFromExpirationTime(mostRecentEventTime);1687  const currentTimeMs: number = now();1688  const timeElapsed = currentTimeMs - eventTimeMs;1689  let msUntilTimeout = jnd(timeElapsed) - timeElapsed;1690  // Compute the time until this render pass would expire.1691  const timeUntilExpirationMs =1692    expirationTimeToMs(committedExpirationTime) - currentTimeMs;1693  // Clamp the timeout to the expiration time.1694  // TODO: Once the event time is exact instead of inferred from expiration time1695  // we don't need this.1696  if (timeUntilExpirationMs < msUntilTimeout) {1697    msUntilTimeout = timeUntilExpirationMs;1698  }1699  // This is the value that is passed to `setTimeout`.1700  return msUntilTimeout;1701}1702function checkForNestedUpdates() {1703  if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {1704    nestedUpdateCount = 0;1705    rootWithNestedUpdates = null;1706    invariant(1707      false,1708      'Maximum update depth exceeded. This can happen when a component ' +1709        'repeatedly calls setState inside componentWillUpdate or ' +1710        'componentDidUpdate. React limits the number of nested updates to ' +1711        'prevent infinite loops.',1712    );1713  }1714  if (__DEV__) {1715    if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {1716      nestedPassiveUpdateCount = 0;1717      warning(1718        false,1719        'Maximum update depth exceeded. This can happen when a component ' +1720          "calls setState inside useEffect, but useEffect either doesn't " +1721          'have a dependency array, or one of the dependencies changes on ' +1722          'every render.',1723      );1724    }1725  }1726}1727function flushRenderPhaseStrictModeWarningsInDEV() {1728  if (__DEV__) {1729    ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();1730    ReactStrictModeWarnings.flushLegacyContextWarning();1731    if (warnAboutDeprecatedLifecycles) {1732      ReactStrictModeWarnings.flushPendingDeprecationWarnings();1733    }1734  }1735}1736function stopFinishedWorkLoopTimer() {1737  const didCompleteRoot = true;1738  stopWorkLoopTimer(interruptedBy, didCompleteRoot);1739  interruptedBy = null;1740}1741function stopInterruptedWorkLoopTimer() {1742  // TODO: Track which fiber caused the interruption.1743  const didCompleteRoot = false;1744  stopWorkLoopTimer(interruptedBy, didCompleteRoot);1745  interruptedBy = null;1746}1747function checkForInterruption(1748  fiberThatReceivedUpdate: Fiber,1749  updateExpirationTime: ExpirationTime,1750) {1751  if (1752    enableUserTimingAPI &&1753    workInProgressRoot !== null &&1754    updateExpirationTime > renderExpirationTime1755  ) {1756    interruptedBy = fiberThatReceivedUpdate;1757  }1758}1759let didWarnStateUpdateForUnmountedComponent: Set<string> | null = null;1760function warnAboutUpdateOnUnmountedFiberInDEV(fiber) {1761  if (__DEV__) {1762    const tag = fiber.tag;1763    if (1764      tag !== HostRoot &&1765      tag !== ClassComponent &&1766      tag !== FunctionComponent &&1767      tag !== ForwardRef &&1768      tag !== MemoComponent &&1769      tag !== SimpleMemoComponent1770    ) {1771      // Only warn for user-defined components, not internal ones like Suspense.1772      return;1773    }1774    // We show the whole stack but dedupe on the top component's name because...ReactFiberScheduler.new.js
Source:ReactFiberScheduler.new.js  
...283  checkForNestedUpdates();284  warnAboutInvalidUpdatesOnClassComponentsInDEV(fiber);285  const root = markUpdateTimeFromFiberToRoot(fiber, expirationTime);286  if (root === null) {287    warnAboutUpdateOnUnmountedFiberInDEV(fiber);288    return;289  }290  root.pingTime = NoWork;291  checkForInterruption(fiber, expirationTime);292  recordScheduleUpdate();293  if (expirationTime === Sync) {294    if (workPhase === LegacyUnbatchedPhase) {295      // This is a legacy edge case. The initial mount of a ReactDOM.render-ed296      // root inside of batchedUpdates should be synchronous, but layout updates297      // should be deferred until the end of the batch.298      let callback = renderRoot(root, Sync, true);299      while (callback !== null) {300        callback = callback(true);301      }302    } else {303      scheduleCallbackForRoot(root, ImmediatePriority, Sync);304      if (workPhase === NotWorking) {305        // Flush the synchronous work now, wnless we're already working or inside306        // a batch. This is intentionally inside scheduleUpdateOnFiber instead of307        // scheduleCallbackForFiber to preserve the ability to schedule a callback308        // without immediately flushing it. We only do this for user-initated309        // updates, to preserve historical behavior of sync mode.310        flushImmediateQueue();311      }312    }313  } else {314    // TODO: computeExpirationForFiber also reads the priority. Pass the315    // priority as an argument to that function and this one.316    const priorityLevel = getCurrentPriorityLevel();317    if (priorityLevel === UserBlockingPriority) {318      // This is the result of a discrete event. Track the lowest priority319      // discrete update per root so we can flush them early, if needed.320      if (rootsWithPendingDiscreteUpdates === null) {321        rootsWithPendingDiscreteUpdates = new Map([[root, expirationTime]]);322      } else {323        const lastDiscreteTime = rootsWithPendingDiscreteUpdates.get(root);324        if (325          lastDiscreteTime === undefined ||326          lastDiscreteTime > expirationTime327        ) {328          rootsWithPendingDiscreteUpdates.set(root, expirationTime);329        }330      }331    }332    scheduleCallbackForRoot(root, priorityLevel, expirationTime);333  }334}335export const scheduleWork = scheduleUpdateOnFiber;336// This is split into a separate function so we can mark a fiber with pending337// work without treating it as a typical update that originates from an event;338// e.g. retrying a Suspense boundary isn't an update, but it does schedule work339// on a fiber.340function markUpdateTimeFromFiberToRoot(fiber, expirationTime) {341  // Update the source fiber's expiration time342  if (fiber.expirationTime < expirationTime) {343    fiber.expirationTime = expirationTime;344  }345  let alternate = fiber.alternate;346  if (alternate !== null && alternate.expirationTime < expirationTime) {347    alternate.expirationTime = expirationTime;348  }349  // Walk the parent path to the root and update the child expiration time.350  let node = fiber.return;351  let root = null;352  if (node === null && fiber.tag === HostRoot) {353    root = fiber.stateNode;354  } else {355    while (node !== null) {356      alternate = node.alternate;357      if (node.childExpirationTime < expirationTime) {358        node.childExpirationTime = expirationTime;359        if (360          alternate !== null &&361          alternate.childExpirationTime < expirationTime362        ) {363          alternate.childExpirationTime = expirationTime;364        }365      } else if (366        alternate !== null &&367        alternate.childExpirationTime < expirationTime368      ) {369        alternate.childExpirationTime = expirationTime;370      }371      if (node.return === null && node.tag === HostRoot) {372        root = node.stateNode;373        break;374      }375      node = node.return;376    }377  }378  if (root !== null) {379    // Update the first and last pending expiration times in this root380    const firstPendingTime = root.firstPendingTime;381    if (expirationTime > firstPendingTime) {382      root.firstPendingTime = expirationTime;383    }384    const lastPendingTime = root.lastPendingTime;385    if (lastPendingTime === NoWork || expirationTime < lastPendingTime) {386      root.lastPendingTime = expirationTime;387    }388  }389  return root;390}391// Use this function, along with runRootCallback, to ensure that only a single392// callback per root is scheduled. It's still possible to call renderRoot393// directly, but scheduling via this function helps avoid excessive callbacks.394// It works by storing the callback node and expiration time on the root. When a395// new callback comes in, it compares the expiration time to determine if it396// should cancel the previous one. It also relies on commitRoot scheduling a397// callback to render the next level, because that means we don't need a398// separate callback per expiration time.399function scheduleCallbackForRoot(400  root: FiberRoot,401  priorityLevel: ReactPriorityLevel,402  expirationTime: ExpirationTime,403) {404  const existingCallbackExpirationTime = root.callbackExpirationTime;405  if (existingCallbackExpirationTime < expirationTime) {406    // New callback has higher priority than the existing one.407    const existingCallbackNode = root.callbackNode;408    if (existingCallbackNode !== null) {409      cancelCallback(existingCallbackNode);410    }411    root.callbackExpirationTime = expirationTime;412    const options =413      expirationTime === Sync414        ? null415        : {timeout: expirationTimeToMs(expirationTime)};416    root.callbackNode = scheduleCallback(417      priorityLevel,418      runRootCallback.bind(419        null,420        root,421        renderRoot.bind(null, root, expirationTime),422      ),423      options,424    );425    if (426      enableUserTimingAPI &&427      expirationTime !== Sync &&428      workPhase !== RenderPhase &&429      workPhase !== CommitPhase430    ) {431      // Scheduled an async callback, and we're not already working. Add an432      // entry to the flamegraph that shows we're waiting for a callback433      // to fire.434      startRequestCallbackTimer();435    }436  }437  const timeoutHandle = root.timeoutHandle;438  if (timeoutHandle !== noTimeout) {439    // The root previous suspended and scheduled a timeout to commit a fallback440    // state. Now that we have additional work, cancel the timeout.441    root.timeoutHandle = noTimeout;442    // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above443    cancelTimeout(timeoutHandle);444  }445  // Add the current set of interactions to the pending set associated with446  // this root.447  schedulePendingInteraction(root, expirationTime);448}449function runRootCallback(root, callback, isSync) {450  const prevCallbackNode = root.callbackNode;451  let continuation = null;452  try {453    continuation = callback(isSync);454    if (continuation !== null) {455      return runRootCallback.bind(null, root, continuation);456    } else {457      return null;458    }459  } finally {460    // If the callback exits without returning a continuation, remove the461    // corresponding callback node from the root. Unless the callback node462    // has changed, which implies that it was already cancelled by a high463    // priority update.464    if (continuation === null && prevCallbackNode === root.callbackNode) {465      root.callbackNode = null;466      root.callbackExpirationTime = NoWork;467    }468  }469}470export function flushRoot(root: FiberRoot, expirationTime: ExpirationTime) {471  if (workPhase === RenderPhase || workPhase === CommitPhase) {472    invariant(473      false,474      'work.commit(): Cannot commit while already rendering. This likely ' +475        'means you attempted to commit from inside a lifecycle method.',476    );477  }478  scheduleCallback(479    ImmediatePriority,480    renderRoot.bind(null, root, expirationTime),481  );482  flushImmediateQueue();483}484export function flushInteractiveUpdates() {485  if (workPhase === RenderPhase || workPhase === CommitPhase) {486    // Can't synchronously flush interactive updates if React is already487    // working. This is currently a no-op.488    // TODO: Should we fire a warning? This happens if you synchronously invoke489    // an input event inside an effect, like with `element.click()`.490    return;491  }492  flushPendingDiscreteUpdates();493}494function resolveLocksOnRoot(root: FiberRoot, expirationTime: ExpirationTime) {495  const firstBatch = root.firstBatch;496  if (497    firstBatch !== null &&498    firstBatch._defer &&499    firstBatch._expirationTime >= expirationTime500  ) {501    root.finishedWork = root.current.alternate;502    root.pendingCommitExpirationTime = expirationTime;503    scheduleCallback(NormalPriority, () => {504      firstBatch._onComplete();505      return null;506    });507    return true;508  } else {509    return false;510  }511}512export function deferredUpdates<A>(fn: () => A): A {513  // TODO: Remove in favor of Scheduler.next514  return runWithPriority(NormalPriority, fn);515}516export function interactiveUpdates<A, B, C, R>(517  fn: (A, B, C) => R,518  a: A,519  b: B,520  c: C,521): R {522  if (workPhase === NotWorking) {523    // TODO: Remove this call. Instead of doing this automatically, the caller524    // should explicitly call flushInteractiveUpdates.525    flushPendingDiscreteUpdates();526  }527  return runWithPriority(UserBlockingPriority, fn.bind(null, a, b, c));528}529export function syncUpdates<A, B, C, R>(530  fn: (A, B, C) => R,531  a: A,532  b: B,533  c: C,534): R {535  return runWithPriority(ImmediatePriority, fn.bind(null, a, b, c));536}537function flushPendingDiscreteUpdates() {538  if (rootsWithPendingDiscreteUpdates !== null) {539    // For each root with pending discrete updates, schedule a callback to540    // immediately flush them.541    const roots = rootsWithPendingDiscreteUpdates;542    rootsWithPendingDiscreteUpdates = null;543    roots.forEach((expirationTime, root) => {544      scheduleCallback(545        ImmediatePriority,546        renderRoot.bind(null, root, expirationTime),547      );548    });549    // Now flush the immediate queue.550    flushImmediateQueue();551  }552}553export function batchedUpdates<A, R>(fn: A => R, a: A): R {554  if (workPhase !== NotWorking) {555    // We're already working, or inside a batch, so batchedUpdates is a no-op.556    return fn(a);557  }558  workPhase = BatchedPhase;559  try {560    return fn(a);561  } finally {562    workPhase = NotWorking;563    // Flush the immediate callbacks that were scheduled during this batch564    flushImmediateQueue();565  }566}567export function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {568  if (workPhase !== BatchedPhase && workPhase !== FlushSyncPhase) {569    // We're not inside batchedUpdates or flushSync, so unbatchedUpdates is570    // a no-op.571    return fn(a);572  }573  const prevWorkPhase = workPhase;574  workPhase = LegacyUnbatchedPhase;575  try {576    return fn(a);577  } finally {578    workPhase = prevWorkPhase;579  }580}581export function flushSync<A, R>(fn: A => R, a: A): R {582  if (workPhase === RenderPhase || workPhase === CommitPhase) {583    invariant(584      false,585      'flushSync was called from inside a lifecycle method. It cannot be ' +586        'called when React is already rendering.',587    );588  }589  const prevWorkPhase = workPhase;590  workPhase = FlushSyncPhase;591  try {592    return runWithPriority(ImmediatePriority, fn.bind(null, a));593  } finally {594    workPhase = prevWorkPhase;595    // Flush the immediate callbacks that were scheduled during this batch.596    // Note that this will happen even if batchedUpdates is higher up597    // the stack.598    flushImmediateQueue();599  }600}601export function flushControlled(fn: () => mixed): void {602  const prevWorkPhase = workPhase;603  workPhase = BatchedPhase;604  try {605    runWithPriority(ImmediatePriority, fn);606  } finally {607    workPhase = prevWorkPhase;608    if (workPhase === NotWorking) {609      // Flush the immediate callbacks that were scheduled during this batch610      flushImmediateQueue();611    }612  }613}614function prepareFreshStack(root, expirationTime) {615  root.pendingCommitExpirationTime = NoWork;616  if (workInProgress !== null) {617    let interruptedWork = workInProgress.return;618    while (interruptedWork !== null) {619      unwindInterruptedWork(interruptedWork);620      interruptedWork = interruptedWork.return;621    }622  }623  workInProgressRoot = root;624  workInProgress = createWorkInProgress(root.current, null, expirationTime);625  renderExpirationTime = expirationTime;626  workInProgressRootExitStatus = RootIncomplete;627  workInProgressRootAbsoluteTimeoutMs = -1;628  if (__DEV__) {629    ReactStrictModeWarnings.discardPendingWarnings();630  }631}632function renderRoot(633  root: FiberRoot,634  expirationTime: ExpirationTime,635  isSync: boolean,636): SchedulerCallback | null {637  invariant(638    workPhase !== RenderPhase && workPhase !== CommitPhase,639    'Should not already be working.',640  );641  if (enableUserTimingAPI && expirationTime !== Sync) {642    const didExpire = isSync;643    const timeoutMs = expirationTimeToMs(expirationTime);644    stopRequestCallbackTimer(didExpire, timeoutMs);645  }646  if (root.firstPendingTime < expirationTime) {647    // If there's no work left at this expiration time, exit immediately. This648    // happens when multiple callbacks are scheduled for a single root, but an649    // earlier callback flushes the work of a later one.650    return null;651  }652  if (root.pendingCommitExpirationTime === expirationTime) {653    // There's already a pending commit at this expiration time.654    root.pendingCommitExpirationTime = NoWork;655    return commitRoot.bind(null, root, expirationTime);656  }657  flushPassiveEffects();658  // If the root or expiration time have changed, throw out the existing stack659  // and prepare a fresh one. Otherwise we'll continue where we left off.660  if (root !== workInProgressRoot || expirationTime !== renderExpirationTime) {661    prepareFreshStack(root, expirationTime);662    startWorkOnPendingInteraction(root, expirationTime);663  }664  // If we have a work-in-progress fiber, it means there's still work to do665  // in this root.666  if (workInProgress !== null) {667    const prevWorkPhase = workPhase;668    workPhase = RenderPhase;669    let prevDispatcher = ReactCurrentDispatcher.current;670    if (prevDispatcher === null) {671      // The React isomorphic package does not include a default dispatcher.672      // Instead the first renderer will lazily attach one, in order to give673      // nicer error messages.674      prevDispatcher = ContextOnlyDispatcher;675    }676    ReactCurrentDispatcher.current = ContextOnlyDispatcher;677    let prevInteractions: Set<Interaction> | null = null;678    if (enableSchedulerTracing) {679      prevInteractions = __interactionsRef.current;680      __interactionsRef.current = root.memoizedInteractions;681    }682    startWorkLoopTimer(workInProgress);683    // TODO: Fork renderRoot into renderRootSync and renderRootAsync684    if (isSync) {685      if (expirationTime !== Sync) {686        // An async update expired. There may be other expired updates on687        // this root. We should render all the expired work in a688        // single batch.689        const currentTime = requestCurrentTime();690        if (currentTime < expirationTime) {691          // Restart at the current time.692          workPhase = prevWorkPhase;693          resetContextDependencies();694          ReactCurrentDispatcher.current = prevDispatcher;695          if (enableSchedulerTracing) {696            __interactionsRef.current = ((prevInteractions: any): Set<697              Interaction,698            >);699          }700          return renderRoot.bind(null, root, currentTime);701        }702      }703    } else {704      // Since we know we're in a React event, we can clear the current705      // event time. The next update will compute a new event time.706      currentEventTime = NoWork;707    }708    do {709      try {710        if (isSync) {711          workLoopSync();712        } else {713          workLoop();714        }715        break;716      } catch (thrownValue) {717        // Reset module-level state that was set during the render phase.718        resetContextDependencies();719        resetHooks();720        const sourceFiber = workInProgress;721        if (sourceFiber === null || sourceFiber.return === null) {722          // Expected to be working on a non-root fiber. This is a fatal error723          // because there's no ancestor that can handle it; the root is724          // supposed to capture all errors that weren't caught by an error725          // boundary.726          prepareFreshStack(root, expirationTime);727          workPhase = prevWorkPhase;728          throw thrownValue;729        }730        if (enableProfilerTimer && sourceFiber.mode & ProfileMode) {731          // Record the time spent rendering before an error was thrown. This732          // avoids inaccurate Profiler durations in the case of a733          // suspended render.734          stopProfilerTimerIfRunningAndRecordDelta(sourceFiber, true);735        }736        const returnFiber = sourceFiber.return;737        throwException(738          root,739          returnFiber,740          sourceFiber,741          thrownValue,742          renderExpirationTime,743        );744        workInProgress = completeUnitOfWork(sourceFiber);745      }746    } while (true);747    workPhase = prevWorkPhase;748    resetContextDependencies();749    ReactCurrentDispatcher.current = prevDispatcher;750    if (enableSchedulerTracing) {751      __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);752    }753    if (workInProgress !== null) {754      // There's still work left over. Return a continuation.755      stopInterruptedWorkLoopTimer();756      if (expirationTime !== Sync) {757        startRequestCallbackTimer();758      }759      return renderRoot.bind(null, root, expirationTime);760    }761  }762  // We now have a consistent tree. The next step is either to commit it, or, if763  // something suspended, wait to commit it after a timeout.764  stopFinishedWorkLoopTimer();765  const isLocked = resolveLocksOnRoot(root, expirationTime);766  if (isLocked) {767    // This root has a lock that prevents it from committing. Exit. If we begin768    // work on the root again, without any intervening updates, it will finish769    // without doing additional work.770    return null;771  }772  // Set this to null to indicate there's no in-progress render.773  workInProgressRoot = null;774  switch (workInProgressRootExitStatus) {775    case RootIncomplete: {776      invariant(false, 'Should have a work-in-progress.');777    }778    // Flow knows about invariant, so it compains if I add a break statement,779    // but eslint doesn't know about invariant, so it complains if I do.780    // eslint-disable-next-line no-fallthrough781    case RootErrored: {782      // An error was thrown. First check if there is lower priority work783      // scheduled on this root.784      const lastPendingTime = root.lastPendingTime;785      if (root.lastPendingTime < expirationTime) {786        // There's lower priority work. Before raising the error, try rendering787        // at the lower priority to see if it fixes it. Use a continuation to788        // maintain the existing priority and position in the queue.789        return renderRoot.bind(null, root, lastPendingTime);790      }791      if (!isSync) {792        // If we're rendering asynchronously, it's possible the error was793        // caused by tearing due to a mutation during an event. Try rendering794        // one more time without yiedling to events.795        prepareFreshStack(root, expirationTime);796        scheduleCallback(797          ImmediatePriority,798          renderRoot.bind(null, root, expirationTime),799        );800        return null;801      }802      // If we're already rendering synchronously, commit the root in its803      // errored state.804      return commitRoot.bind(null, root, expirationTime);805    }806    case RootSuspended: {807      const lastPendingTime = root.lastPendingTime;808      if (root.lastPendingTime < expirationTime) {809        // There's lower priority work. It might be unsuspended. Try rendering810        // at that level.811        return renderRoot.bind(null, root, lastPendingTime);812      }813      if (!isSync) {814        const msUntilTimeout = computeMsUntilTimeout(815          root,816          workInProgressRootAbsoluteTimeoutMs,817        );818        if (msUntilTimeout > 0) {819          // The render is suspended, it hasn't timed out, and there's no lower820          // priority work to do. Instead of committing the fallback821          // immediately, wait for more data to arrive.822          root.timeoutHandle = scheduleTimeout(823            commitRoot.bind(null, root, expirationTime),824            msUntilTimeout,825          );826          return null;827        }828      }829      // The work expired. Commit immediately.830      return commitRoot.bind(null, root, expirationTime);831    }832    case RootCompleted: {833      // The work completed. Ready to commit.834      return commitRoot.bind(null, root, expirationTime);835    }836    default: {837      invariant(false, 'Unknown root exit status.');838    }839  }840}841export function renderDidSuspend(842  root: FiberRoot,843  absoluteTimeoutMs: number,844  // TODO: Don't need this argument anymore845  suspendedTime: ExpirationTime,846) {847  if (848    absoluteTimeoutMs >= 0 &&849    workInProgressRootAbsoluteTimeoutMs < absoluteTimeoutMs850  ) {851    workInProgressRootAbsoluteTimeoutMs = absoluteTimeoutMs;852    if (workInProgressRootExitStatus === RootIncomplete) {853      workInProgressRootExitStatus = RootSuspended;854    }855  }856}857export function renderDidError() {858  if (859    workInProgressRootExitStatus === RootIncomplete ||860    workInProgressRootExitStatus === RootSuspended861  ) {862    workInProgressRootExitStatus = RootErrored;863  }864}865function workLoopSync() {866  // Already timed out, so perform work without checking if we need to yield.867  while (workInProgress !== null) {868    workInProgress = performUnitOfWork(workInProgress);869  }870}871function workLoop() {872  // Perform work until Scheduler asks us to yield873  while (workInProgress !== null && !shouldYield()) {874    workInProgress = performUnitOfWork(workInProgress);875  }876}877function performUnitOfWork(unitOfWork: Fiber): Fiber | null {878  // The current, flushed, state of this fiber is the alternate. Ideally879  // nothing should rely on this, but relying on it here means that we don't880  // need an additional field on the work in progress.881  const current = unitOfWork.alternate;882  startWorkTimer(unitOfWork);883  setCurrentDebugFiberInDEV(unitOfWork);884  let next;885  if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoContext) {886    startProfilerTimer(unitOfWork);887    next = beginWork(current, unitOfWork, renderExpirationTime);888    stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);889  } else {890    next = beginWork(current, unitOfWork, renderExpirationTime);891  }892  resetCurrentDebugFiberInDEV();893  unitOfWork.memoizedProps = unitOfWork.pendingProps;894  if (next === null) {895    // If this doesn't spawn new work, complete the current work.896    next = completeUnitOfWork(unitOfWork);897  }898  ReactCurrentOwner.current = null;899  return next;900}901function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {902  // Attempt to complete the current unit of work, then move to the next903  // sibling. If there are no more siblings, return to the parent fiber.904  workInProgress = unitOfWork;905  do {906    // The current, flushed, state of this fiber is the alternate. Ideally907    // nothing should rely on this, but relying on it here means that we don't908    // need an additional field on the work in progress.909    const current = workInProgress.alternate;910    const returnFiber = workInProgress.return;911    // Check if the work completed or if something threw.912    if ((workInProgress.effectTag & Incomplete) === NoEffect) {913      setCurrentDebugFiberInDEV(workInProgress);914      let next;915      if (916        !enableProfilerTimer ||917        (workInProgress.mode & ProfileMode) === NoContext918      ) {919        next = completeWork(current, workInProgress, renderExpirationTime);920      } else {921        startProfilerTimer(workInProgress);922        next = completeWork(current, workInProgress, renderExpirationTime);923        // Update render duration assuming we didn't error.924        stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);925      }926      stopWorkTimer(workInProgress);927      resetCurrentDebugFiberInDEV();928      resetChildExpirationTime(workInProgress);929      if (next !== null) {930        // Completing this fiber spawned new work. Work on that next.931        return next;932      }933      if (934        returnFiber !== null &&935        // Do not append effects to parents if a sibling failed to complete936        (returnFiber.effectTag & Incomplete) === NoEffect937      ) {938        // Append all the effects of the subtree and this fiber onto the effect939        // list of the parent. The completion order of the children affects the940        // side-effect order.941        if (returnFiber.firstEffect === null) {942          returnFiber.firstEffect = workInProgress.firstEffect;943        }944        if (workInProgress.lastEffect !== null) {945          if (returnFiber.lastEffect !== null) {946            returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;947          }948          returnFiber.lastEffect = workInProgress.lastEffect;949        }950        // If this fiber had side-effects, we append it AFTER the children's951        // side-effects. We can perform certain side-effects earlier if needed,952        // by doing multiple passes over the effect list. We don't want to953        // schedule our own side-effect on our own list because if end up954        // reusing children we'll schedule this effect onto itself since we're955        // at the end.956        const effectTag = workInProgress.effectTag;957        // Skip both NoWork and PerformedWork tags when creating the effect958        // list. PerformedWork effect is read by React DevTools but shouldn't be959        // committed.960        if (effectTag > PerformedWork) {961          if (returnFiber.lastEffect !== null) {962            returnFiber.lastEffect.nextEffect = workInProgress;963          } else {964            returnFiber.firstEffect = workInProgress;965          }966          returnFiber.lastEffect = workInProgress;967        }968      }969    } else {970      // This fiber did not complete because something threw. Pop values off971      // the stack without entering the complete phase. If this is a boundary,972      // capture values if possible.973      const next = unwindWork(workInProgress, renderExpirationTime);974      // Because this fiber did not complete, don't reset its expiration time.975      if (976        enableProfilerTimer &&977        (workInProgress.mode & ProfileMode) !== NoContext978      ) {979        // Record the render duration for the fiber that errored.980        stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);981        // Include the time spent working on failed children before continuing.982        let actualDuration = workInProgress.actualDuration;983        let child = workInProgress.child;984        while (child !== null) {985          actualDuration += child.actualDuration;986          child = child.sibling;987        }988        workInProgress.actualDuration = actualDuration;989      }990      if (next !== null) {991        // If completing this work spawned new work, do that next. We'll come992        // back here again.993        // Since we're restarting, remove anything that is not a host effect994        // from the effect tag.995        // TODO: The name stopFailedWorkTimer is misleading because Suspense996        // also captures and restarts.997        stopFailedWorkTimer(workInProgress);998        next.effectTag &= HostEffectMask;999        return next;1000      }1001      stopWorkTimer(workInProgress);1002      if (returnFiber !== null) {1003        // Mark the parent fiber as incomplete and clear its effect list.1004        returnFiber.firstEffect = returnFiber.lastEffect = null;1005        returnFiber.effectTag |= Incomplete;1006      }1007    }1008    const siblingFiber = workInProgress.sibling;1009    if (siblingFiber !== null) {1010      // If there is more work to do in this returnFiber, do that next.1011      return siblingFiber;1012    }1013    // Otherwise, return to the parent1014    workInProgress = returnFiber;1015  } while (workInProgress !== null);1016  // We've reached the root.1017  if (workInProgressRootExitStatus === RootIncomplete) {1018    workInProgressRootExitStatus = RootCompleted;1019  }1020  return null;1021}1022function resetChildExpirationTime(completedWork: Fiber) {1023  if (1024    renderExpirationTime !== Never &&1025    completedWork.childExpirationTime === Never1026  ) {1027    // The children of this component are hidden. Don't bubble their1028    // expiration times.1029    return;1030  }1031  let newChildExpirationTime = NoWork;1032  // Bubble up the earliest expiration time.1033  if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoContext) {1034    // In profiling mode, resetChildExpirationTime is also used to reset1035    // profiler durations.1036    let actualDuration = completedWork.actualDuration;1037    let treeBaseDuration = completedWork.selfBaseDuration;1038    // When a fiber is cloned, its actualDuration is reset to 0. This value will1039    // only be updated if work is done on the fiber (i.e. it doesn't bailout).1040    // When work is done, it should bubble to the parent's actualDuration. If1041    // the fiber has not been cloned though, (meaning no work was done), then1042    // this value will reflect the amount of time spent working on a previous1043    // render. In that case it should not bubble. We determine whether it was1044    // cloned by comparing the child pointer.1045    const shouldBubbleActualDurations =1046      completedWork.alternate === null ||1047      completedWork.child !== completedWork.alternate.child;1048    let child = completedWork.child;1049    while (child !== null) {1050      const childUpdateExpirationTime = child.expirationTime;1051      const childChildExpirationTime = child.childExpirationTime;1052      if (childUpdateExpirationTime > newChildExpirationTime) {1053        newChildExpirationTime = childUpdateExpirationTime;1054      }1055      if (childChildExpirationTime > newChildExpirationTime) {1056        newChildExpirationTime = childChildExpirationTime;1057      }1058      if (shouldBubbleActualDurations) {1059        actualDuration += child.actualDuration;1060      }1061      treeBaseDuration += child.treeBaseDuration;1062      child = child.sibling;1063    }1064    completedWork.actualDuration = actualDuration;1065    completedWork.treeBaseDuration = treeBaseDuration;1066  } else {1067    let child = completedWork.child;1068    while (child !== null) {1069      const childUpdateExpirationTime = child.expirationTime;1070      const childChildExpirationTime = child.childExpirationTime;1071      if (childUpdateExpirationTime > newChildExpirationTime) {1072        newChildExpirationTime = childUpdateExpirationTime;1073      }1074      if (childChildExpirationTime > newChildExpirationTime) {1075        newChildExpirationTime = childChildExpirationTime;1076      }1077      child = child.sibling;1078    }1079  }1080  completedWork.childExpirationTime = newChildExpirationTime;1081}1082function commitRoot(root, expirationTime) {1083  runWithPriority(1084    ImmediatePriority,1085    commitRootImpl.bind(null, root, expirationTime),1086  );1087  // If there are passive effects, schedule a callback to flush them. This goes1088  // outside commitRootImpl so that it inherits the priority of the render.1089  if (rootWithPendingPassiveEffects !== null) {1090    const priorityLevel = getCurrentPriorityLevel();1091    scheduleCallback(priorityLevel, () => {1092      flushPassiveEffects();1093      return null;1094    });1095  }1096  return null;1097}1098function commitRootImpl(root, expirationTime) {1099  flushPassiveEffects();1100  flushRenderPhaseStrictModeWarningsInDEV();1101  invariant(1102    workPhase !== RenderPhase && workPhase !== CommitPhase,1103    'Should not already be working.',1104  );1105  const finishedWork = root.current.alternate;1106  invariant(finishedWork !== null, 'Should have a work-in-progress root.');1107  // commitRoot never returns a continuation; it always finishes synchronously.1108  // So we can clear these now to allow a new callback to be scheduled.1109  root.callbackNode = null;1110  root.callbackExpirationTime = NoWork;1111  startCommitTimer();1112  // Update the first and last pending times on this root. The new first1113  // pending time is whatever is left on the root fiber.1114  const updateExpirationTimeBeforeCommit = finishedWork.expirationTime;1115  const childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;1116  const firstPendingTimeBeforeCommit =1117    childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit1118      ? childExpirationTimeBeforeCommit1119      : updateExpirationTimeBeforeCommit;1120  root.firstPendingTime = firstPendingTimeBeforeCommit;1121  if (firstPendingTimeBeforeCommit < root.lastPendingTime) {1122    // This usually means we've finished all the work, but it can also happen1123    // when something gets downprioritized during render, like a hidden tree.1124    root.lastPendingTime = firstPendingTimeBeforeCommit;1125  }1126  if (root === workInProgressRoot) {1127    // We can reset these now that they are finished.1128    workInProgressRoot = null;1129    workInProgress = null;1130    renderExpirationTime = NoWork;1131  } else {1132    // This indicates that the last root we worked on is not the same one that1133    // we're committing now. This most commonly happens when a suspended root1134    // times out.1135  }1136  // Get the list of effects.1137  let firstEffect;1138  if (finishedWork.effectTag > PerformedWork) {1139    // A fiber's effect list consists only of its children, not itself. So if1140    // the root has an effect, we need to add it to the end of the list. The1141    // resulting list is the set that would belong to the root's parent, if it1142    // had one; that is, all the effects in the tree including the root.1143    if (finishedWork.lastEffect !== null) {1144      finishedWork.lastEffect.nextEffect = finishedWork;1145      firstEffect = finishedWork.firstEffect;1146    } else {1147      firstEffect = finishedWork;1148    }1149  } else {1150    // There is no effect on the root.1151    firstEffect = finishedWork.firstEffect;1152  }1153  if (firstEffect !== null) {1154    const prevWorkPhase = workPhase;1155    workPhase = CommitPhase;1156    let prevInteractions: Set<Interaction> | null = null;1157    if (enableSchedulerTracing) {1158      prevInteractions = __interactionsRef.current;1159      __interactionsRef.current = root.memoizedInteractions;1160    }1161    // Reset this to null before calling lifecycles1162    ReactCurrentOwner.current = null;1163    // The commit phase is broken into several sub-phases. We do a separate pass1164    // of the effect list for each phase: all mutation effects come before all1165    // layout effects, and so on.1166    // The first phase a "before mutation" phase. We use this phase to read the1167    // state of the host tree right before we mutate it. This is where1168    // getSnapshotBeforeUpdate is called.1169    startCommitSnapshotEffectsTimer();1170    prepareForCommit(root.containerInfo);1171    nextEffect = firstEffect;1172    do {1173      if (__DEV__) {1174        invokeGuardedCallback(null, commitBeforeMutationEffects, null);1175        if (hasCaughtError()) {1176          invariant(nextEffect !== null, 'Should be working on an effect.');1177          const error = clearCaughtError();1178          captureCommitPhaseError(nextEffect, error);1179          nextEffect = nextEffect.nextEffect;1180        }1181      } else {1182        try {1183          commitBeforeMutationEffects();1184        } catch (error) {1185          invariant(nextEffect !== null, 'Should be working on an effect.');1186          captureCommitPhaseError(nextEffect, error);1187          nextEffect = nextEffect.nextEffect;1188        }1189      }1190    } while (nextEffect !== null);1191    stopCommitSnapshotEffectsTimer();1192    if (enableProfilerTimer) {1193      // Mark the current commit time to be shared by all Profilers in this1194      // batch. This enables them to be grouped later.1195      recordCommitTime();1196    }1197    // The next phase is the mutation phase, where we mutate the host tree.1198    startCommitHostEffectsTimer();1199    nextEffect = firstEffect;1200    do {1201      if (__DEV__) {1202        invokeGuardedCallback(null, commitMutationEffects, null);1203        if (hasCaughtError()) {1204          invariant(nextEffect !== null, 'Should be working on an effect.');1205          const error = clearCaughtError();1206          captureCommitPhaseError(nextEffect, error);1207          nextEffect = nextEffect.nextEffect;1208        }1209      } else {1210        try {1211          commitMutationEffects();1212        } catch (error) {1213          invariant(nextEffect !== null, 'Should be working on an effect.');1214          captureCommitPhaseError(nextEffect, error);1215          nextEffect = nextEffect.nextEffect;1216        }1217      }1218    } while (nextEffect !== null);1219    stopCommitHostEffectsTimer();1220    resetAfterCommit(root.containerInfo);1221    // The work-in-progress tree is now the current tree. This must come after1222    // the mutation phase, so that the previous tree is still current during1223    // componentWillUnmount, but before the layout phase, so that the finished1224    // work is current during componentDidMount/Update.1225    root.current = finishedWork;1226    // The next phase is the layout phase, where we call effects that read1227    // the host tree after it's been mutated. The idiomatic use case for this is1228    // layout, but class component lifecycles also fire here for legacy reasons.1229    startCommitLifeCyclesTimer();1230    nextEffect = firstEffect;1231    do {1232      if (__DEV__) {1233        invokeGuardedCallback(1234          null,1235          commitLayoutEffects,1236          null,1237          root,1238          expirationTime,1239        );1240        if (hasCaughtError()) {1241          invariant(nextEffect !== null, 'Should be working on an effect.');1242          const error = clearCaughtError();1243          captureCommitPhaseError(nextEffect, error);1244          nextEffect = nextEffect.nextEffect;1245        }1246      } else {1247        try {1248          commitLayoutEffects(root, expirationTime);1249        } catch (error) {1250          invariant(nextEffect !== null, 'Should be working on an effect.');1251          captureCommitPhaseError(nextEffect, error);1252          nextEffect = nextEffect.nextEffect;1253        }1254      }1255    } while (nextEffect !== null);1256    stopCommitLifeCyclesTimer();1257    nextEffect = null;1258    if (enableSchedulerTracing) {1259      __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1260    }1261    workPhase = prevWorkPhase;1262  } else {1263    // No effects.1264    root.current = finishedWork;1265    // Measure these anyway so the flamegraph explicitly shows that there were1266    // no effects.1267    // TODO: Maybe there's a better way to report this.1268    startCommitSnapshotEffectsTimer();1269    stopCommitSnapshotEffectsTimer();1270    if (enableProfilerTimer) {1271      recordCommitTime();1272    }1273    startCommitHostEffectsTimer();1274    stopCommitHostEffectsTimer();1275    startCommitLifeCyclesTimer();1276    stopCommitLifeCyclesTimer();1277  }1278  stopCommitTimer();1279  if (rootDoesHavePassiveEffects) {1280    // This commit has passive effects. Stash a reference to them. But don't1281    // schedule a callback until after flushing layout work.1282    rootDoesHavePassiveEffects = false;1283    rootWithPendingPassiveEffects = root;1284    pendingPassiveEffectsExpirationTime = expirationTime;1285  } else {1286    if (enableSchedulerTracing) {1287      // If there are no passive effects, then we can complete the pending1288      // interactions. Otherwise, we'll wait until after the passive effects1289      // are flushed.1290      finishPendingInteractions(root, expirationTime);1291    }1292  }1293  // Check if there's remaining work on this root1294  const remainingExpirationTime = root.firstPendingTime;1295  if (remainingExpirationTime !== NoWork) {1296    const currentTime = requestCurrentTime();1297    const priorityLevel = inferPriorityFromExpirationTime(1298      currentTime,1299      remainingExpirationTime,1300    );1301    scheduleCallbackForRoot(root, priorityLevel, remainingExpirationTime);1302  } else {1303    // If there's no remaining work, we can clear the set of already failed1304    // error boundaries.1305    legacyErrorBoundariesThatAlreadyFailed = null;1306  }1307  onCommitRoot(finishedWork.stateNode);1308  if (remainingExpirationTime === Sync) {1309    // Count the number of times the root synchronously re-renders without1310    // finishing. If there are too many, it indicates an infinite update loop.1311    if (root === rootWithNestedUpdates) {1312      nestedUpdateCount++;1313    } else {1314      nestedUpdateCount = 0;1315      rootWithNestedUpdates = root;1316    }1317  } else {1318    nestedUpdateCount = 0;1319  }1320  if (hasUncaughtError) {1321    hasUncaughtError = false;1322    const error = firstUncaughtError;1323    firstUncaughtError = null;1324    throw error;1325  }1326  if (workPhase === LegacyUnbatchedPhase) {1327    // This is a legacy edge case. We just committed the initial mount of1328    // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1329    // synchronously, but layout updates should be deferred until the end1330    // of the batch.1331    return null;1332  }1333  // If layout work was scheduled, flush it now.1334  flushImmediateQueue();1335  return null;1336}1337function commitBeforeMutationEffects() {1338  while (nextEffect !== null) {1339    if ((nextEffect.effectTag & Snapshot) !== NoEffect) {1340      setCurrentDebugFiberInDEV(nextEffect);1341      recordEffect();1342      const current = nextEffect.alternate;1343      commitBeforeMutationEffectOnFiber(current, nextEffect);1344      resetCurrentDebugFiberInDEV();1345    }1346    nextEffect = nextEffect.nextEffect;1347  }1348}1349function commitMutationEffects() {1350  // TODO: Should probably move the bulk of this function to commitWork.1351  while (nextEffect !== null) {1352    setCurrentDebugFiberInDEV(nextEffect);1353    const effectTag = nextEffect.effectTag;1354    if (effectTag & ContentReset) {1355      commitResetTextContent(nextEffect);1356    }1357    if (effectTag & Ref) {1358      const current = nextEffect.alternate;1359      if (current !== null) {1360        commitDetachRef(current);1361      }1362    }1363    // The following switch statement is only concerned about placement,1364    // updates, and deletions. To avoid needing to add a case for every possible1365    // bitmap value, we remove the secondary effects from the effect tag and1366    // switch on that value.1367    let primaryEffectTag = effectTag & (Placement | Update | Deletion);1368    switch (primaryEffectTag) {1369      case Placement: {1370        commitPlacement(nextEffect);1371        // Clear the "placement" from effect tag so that we know that this is1372        // inserted, before any life-cycles like componentDidMount gets called.1373        // TODO: findDOMNode doesn't rely on this any more but isMounted does1374        // and isMounted is deprecated anyway so we should be able to kill this.1375        nextEffect.effectTag &= ~Placement;1376        break;1377      }1378      case PlacementAndUpdate: {1379        // Placement1380        commitPlacement(nextEffect);1381        // Clear the "placement" from effect tag so that we know that this is1382        // inserted, before any life-cycles like componentDidMount gets called.1383        nextEffect.effectTag &= ~Placement;1384        // Update1385        const current = nextEffect.alternate;1386        commitWork(current, nextEffect);1387        break;1388      }1389      case Update: {1390        const current = nextEffect.alternate;1391        commitWork(current, nextEffect);1392        break;1393      }1394      case Deletion: {1395        commitDeletion(nextEffect);1396        break;1397      }1398    }1399    // TODO: Only record a mutation effect if primaryEffectTag is non-zero.1400    recordEffect();1401    resetCurrentDebugFiberInDEV();1402    nextEffect = nextEffect.nextEffect;1403  }1404}1405function commitLayoutEffects(1406  root: FiberRoot,1407  committedExpirationTime: ExpirationTime,1408) {1409  // TODO: Should probably move the bulk of this function to commitWork.1410  while (nextEffect !== null) {1411    setCurrentDebugFiberInDEV(nextEffect);1412    const effectTag = nextEffect.effectTag;1413    if (effectTag & (Update | Callback)) {1414      recordEffect();1415      const current = nextEffect.alternate;1416      commitLayoutEffectOnFiber(1417        root,1418        current,1419        nextEffect,1420        committedExpirationTime,1421      );1422    }1423    if (effectTag & Ref) {1424      recordEffect();1425      commitAttachRef(nextEffect);1426    }1427    if (effectTag & Passive) {1428      rootDoesHavePassiveEffects = true;1429    }1430    resetCurrentDebugFiberInDEV();1431    nextEffect = nextEffect.nextEffect;1432  }1433}1434export function flushPassiveEffects() {1435  if (rootWithPendingPassiveEffects === null) {1436    return false;1437  }1438  const root = rootWithPendingPassiveEffects;1439  const expirationTime = pendingPassiveEffectsExpirationTime;1440  rootWithPendingPassiveEffects = null;1441  pendingPassiveEffectsExpirationTime = NoWork;1442  let prevInteractions: Set<Interaction> | null = null;1443  if (enableSchedulerTracing) {1444    prevInteractions = __interactionsRef.current;1445    __interactionsRef.current = root.memoizedInteractions;1446  }1447  invariant(1448    workPhase !== RenderPhase && workPhase !== CommitPhase,1449    'Cannot flush passive effects while already rendering.',1450  );1451  const prevWorkPhase = workPhase;1452  workPhase = CommitPhase;1453  // Note: This currently assumes there are no passive effects on the root1454  // fiber, because the root is not part of its own effect list. This could1455  // change in the future.1456  let effect = root.current.firstEffect;1457  while (effect !== null) {1458    if (__DEV__) {1459      setCurrentDebugFiberInDEV(effect);1460      invokeGuardedCallback(null, commitPassiveHookEffects, null, effect);1461      if (hasCaughtError()) {1462        invariant(effect !== null, 'Should be working on an effect.');1463        const error = clearCaughtError();1464        captureCommitPhaseError(effect, error);1465      }1466      resetCurrentDebugFiberInDEV();1467    } else {1468      try {1469        commitPassiveHookEffects(effect);1470      } catch (error) {1471        invariant(effect !== null, 'Should be working on an effect.');1472        captureCommitPhaseError(effect, error);1473      }1474    }1475    effect = effect.nextEffect;1476  }1477  if (enableSchedulerTracing) {1478    __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1479    finishPendingInteractions(root, expirationTime);1480  }1481  workPhase = prevWorkPhase;1482  flushImmediateQueue();1483  // If additional passive effects were scheduled, increment a counter. If this1484  // exceeds the limit, we'll fire a warning.1485  nestedPassiveUpdateCount =1486    rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1487  return true;1488}1489export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {1490  return (1491    legacyErrorBoundariesThatAlreadyFailed !== null &&1492    legacyErrorBoundariesThatAlreadyFailed.has(instance)1493  );1494}1495export function markLegacyErrorBoundaryAsFailed(instance: mixed) {1496  if (legacyErrorBoundariesThatAlreadyFailed === null) {1497    legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1498  } else {1499    legacyErrorBoundariesThatAlreadyFailed.add(instance);1500  }1501}1502function prepareToThrowUncaughtError(error: mixed) {1503  if (!hasUncaughtError) {1504    hasUncaughtError = true;1505    firstUncaughtError = error;1506  }1507}1508export const onUncaughtError = prepareToThrowUncaughtError;1509function captureCommitPhaseErrorOnRoot(1510  rootFiber: Fiber,1511  sourceFiber: Fiber,1512  error: mixed,1513) {1514  const errorInfo = createCapturedValue(error, sourceFiber);1515  const update = createRootErrorUpdate(rootFiber, errorInfo, Sync);1516  enqueueUpdate(rootFiber, update);1517  const root = markUpdateTimeFromFiberToRoot(rootFiber, Sync);1518  if (root !== null) {1519    scheduleCallbackForRoot(root, ImmediatePriority, Sync);1520  }1521}1522export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {1523  if (sourceFiber.tag === HostRoot) {1524    // Error was thrown at the root. There is no parent, so the root1525    // itself should capture it.1526    captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1527    return;1528  }1529  let fiber = sourceFiber.return;1530  while (fiber !== null) {1531    if (fiber.tag === HostRoot) {1532      captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1533      return;1534    } else if (fiber.tag === ClassComponent) {1535      const ctor = fiber.type;1536      const instance = fiber.stateNode;1537      if (1538        typeof ctor.getDerivedStateFromError === 'function' ||1539        (typeof instance.componentDidCatch === 'function' &&1540          !isAlreadyFailedLegacyErrorBoundary(instance))1541      ) {1542        const errorInfo = createCapturedValue(error, sourceFiber);1543        const update = createClassErrorUpdate(1544          fiber,1545          errorInfo,1546          // TODO: This is always sync1547          Sync,1548        );1549        enqueueUpdate(fiber, update);1550        const root = markUpdateTimeFromFiberToRoot(fiber, Sync);1551        if (root !== null) {1552          scheduleCallbackForRoot(root, ImmediatePriority, Sync);1553        }1554        return;1555      }1556    }1557    fiber = fiber.return;1558  }1559}1560export function pingSuspendedRoot(1561  root: FiberRoot,1562  thenable: Thenable,1563  suspendedTime: ExpirationTime,1564) {1565  const pingCache = root.pingCache;1566  if (pingCache !== null) {1567    // The thenable resolved, so we no longer need to memoize, because it will1568    // never be thrown again.1569    pingCache.delete(thenable);1570  }1571  if (workInProgressRoot === root && renderExpirationTime === suspendedTime) {1572    // Received a ping at the same priority level at which we're currently1573    // rendering. Restart from the root. Don't need to schedule a ping because1574    // we're already working on this tree.1575    prepareFreshStack(root, renderExpirationTime);1576    return;1577  }1578  const lastPendingTime = root.lastPendingTime;1579  if (lastPendingTime < suspendedTime) {1580    // The root is no longer suspended at this time.1581    return;1582  }1583  const pingTime = root.pingTime;1584  if (pingTime !== NoWork && pingTime < suspendedTime) {1585    // There's already a lower priority ping scheduled.1586    return;1587  }1588  // Mark the time at which this ping was scheduled.1589  root.pingTime = suspendedTime;1590  const currentTime = requestCurrentTime();1591  const priorityLevel = inferPriorityFromExpirationTime(1592    currentTime,1593    suspendedTime,1594  );1595  scheduleCallbackForRoot(root, priorityLevel, suspendedTime);1596}1597export function retryTimedOutBoundary(boundaryFiber: Fiber) {1598  // The boundary fiber (a Suspense component) previously timed out and was1599  // rendered in its fallback state. One of the promises that suspended it has1600  // resolved, which means at least part of the tree was likely unblocked. Try1601  // rendering again, at a new expiration time.1602  const currentTime = requestCurrentTime();1603  const retryTime = computeExpirationForFiber(currentTime, boundaryFiber);1604  // TODO: Special case idle priority?1605  const priorityLevel = inferPriorityFromExpirationTime(currentTime, retryTime);1606  const root = markUpdateTimeFromFiberToRoot(boundaryFiber, retryTime);1607  if (root !== null) {1608    scheduleCallbackForRoot(root, priorityLevel, retryTime);1609  }1610}1611export function resolveRetryThenable(boundaryFiber: Fiber, thenable: Thenable) {1612  let retryCache: WeakSet<Thenable> | Set<Thenable> | null;1613  if (enableSuspenseServerRenderer) {1614    switch (boundaryFiber.tag) {1615      case SuspenseComponent:1616        retryCache = boundaryFiber.stateNode;1617        break;1618      case DehydratedSuspenseComponent:1619        retryCache = boundaryFiber.memoizedState;1620        break;1621      default:1622        invariant(1623          false,1624          'Pinged unknown suspense boundary type. ' +1625            'This is probably a bug in React.',1626        );1627    }1628  } else {1629    retryCache = boundaryFiber.stateNode;1630  }1631  if (retryCache !== null) {1632    // The thenable resolved, so we no longer need to memoize, because it will1633    // never be thrown again.1634    retryCache.delete(thenable);1635  }1636  retryTimedOutBoundary(boundaryFiber);1637}1638export function inferStartTimeFromExpirationTime(1639  root: FiberRoot,1640  expirationTime: ExpirationTime,1641) {1642  // We don't know exactly when the update was scheduled, but we can infer an1643  // approximate start time from the expiration time.1644  const earliestExpirationTimeMs = expirationTimeToMs(root.firstPendingTime);1645  // TODO: Track this on the root instead. It's more accurate, doesn't rely on1646  // assumptions about priority, and isn't coupled to Scheduler details.1647  return earliestExpirationTimeMs - LOW_PRIORITY_EXPIRATION;1648}1649function computeMsUntilTimeout(root, absoluteTimeoutMs) {1650  if (disableYielding) {1651    // Timeout immediately when yielding is disabled.1652    return 0;1653  }1654  // Find the earliest uncommitted expiration time in the tree, including1655  // work that is suspended. The timeout threshold cannot be longer than1656  // the overall expiration.1657  const earliestExpirationTimeMs = expirationTimeToMs(root.firstPendingTime);1658  if (earliestExpirationTimeMs < absoluteTimeoutMs) {1659    absoluteTimeoutMs = earliestExpirationTimeMs;1660  }1661  // Subtract the current time from the absolute timeout to get the number1662  // of milliseconds until the timeout. In other words, convert an absolute1663  // timestamp to a relative time. This is the value that is passed1664  // to `setTimeout`.1665  let msUntilTimeout = absoluteTimeoutMs - now();1666  return msUntilTimeout < 0 ? 0 : msUntilTimeout;1667}1668function checkForNestedUpdates() {1669  if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {1670    nestedUpdateCount = 0;1671    rootWithNestedUpdates = null;1672    invariant(1673      false,1674      'Maximum update depth exceeded. This can happen when a component ' +1675        'repeatedly calls setState inside componentWillUpdate or ' +1676        'componentDidUpdate. React limits the number of nested updates to ' +1677        'prevent infinite loops.',1678    );1679  }1680  if (__DEV__) {1681    if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {1682      nestedPassiveUpdateCount = 0;1683      warning(1684        false,1685        'Maximum update depth exceeded. This can happen when a component ' +1686          "calls setState inside useEffect, but useEffect either doesn't " +1687          'have a dependency array, or one of the dependencies changes on ' +1688          'every render.',1689      );1690    }1691  }1692}1693function flushRenderPhaseStrictModeWarningsInDEV() {1694  if (__DEV__) {1695    ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();1696    ReactStrictModeWarnings.flushLegacyContextWarning();1697    if (warnAboutDeprecatedLifecycles) {1698      ReactStrictModeWarnings.flushPendingDeprecationWarnings();1699    }1700  }1701}1702function stopFinishedWorkLoopTimer() {1703  const didCompleteRoot = true;1704  stopWorkLoopTimer(interruptedBy, didCompleteRoot);1705  interruptedBy = null;1706}1707function stopInterruptedWorkLoopTimer() {1708  // TODO: Track which fiber caused the interruption.1709  const didCompleteRoot = false;1710  stopWorkLoopTimer(interruptedBy, didCompleteRoot);1711  interruptedBy = null;1712}1713function checkForInterruption(1714  fiberThatReceivedUpdate: Fiber,1715  updateExpirationTime: ExpirationTime,1716) {1717  if (1718    enableUserTimingAPI &&1719    workInProgressRoot !== null &&1720    updateExpirationTime > renderExpirationTime1721  ) {1722    interruptedBy = fiberThatReceivedUpdate;1723  }1724}1725let didWarnStateUpdateForUnmountedComponent: Set<string> | null = null;1726function warnAboutUpdateOnUnmountedFiberInDEV(fiber) {1727  if (__DEV__) {1728    const tag = fiber.tag;1729    if (1730      tag !== HostRoot &&1731      tag !== ClassComponent &&1732      tag !== FunctionComponent &&1733      tag !== ForwardRef &&1734      tag !== MemoComponent &&1735      tag !== SimpleMemoComponent1736    ) {1737      // Only warn for user-defined components, not internal ones like Suspense.1738      return;1739    }1740    // We show the whole stack but dedupe on the top component's name because...ReactFiberWorkLoop.js
Source:ReactFiberWorkLoop.js  
...215  // checkForNestedUpdates()216  // warnAboutRenderPhaseUpdatesInDEV(fiber);217  const root = markUpdateLaneFromFiberToRoot(fiber, lane);218  if (root === null) {219    //   warnAboutUpdateOnUnmountedFiberInDEV(fiber);220    return null;221  }222  // Mark that the root has a pending update.223  markRootUpdated(root, lane, eventTime);224  // if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {225  //     // ...226  // }227  if (root === workInProgressRoot) {228    // Received an update to a tree that's in the middle of rendering. Mark229    // that there was an interleaved update work on this root. Unless the230    // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render231    // phase update. In that case, we don't treat render phase updates as if232    // they were interleaved, for backwards compat reasons.233    if (...scheduleUpdateOnFiber.js
Source:scheduleUpdateOnFiber.js  
2    checkForNestedUpdates();3    warnAboutInvalidUpdatesOnClassComponentsInDEV(fiber);4    var root = markUpdateTimeFromFiberToRoot(fiber, expirationTime);5    if (root === null) {6        warnAboutUpdateOnUnmountedFiberInDEV(fiber);7        return;8    }9    checkForInterruption(fiber, expirationTime);10    recordScheduleUpdate(); // TODO: computeExpirationForFiber also reads the priority. Pass the11    // priority as an argument to that function and this one.12    var priorityLevel = getCurrentPriorityLevel();13    if (expirationTime === Sync) {14        if ( // Check if we're inside unbatchedUpdates15            (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering16            (executionContext & (RenderContext | CommitContext)) === NoContext) {17            // Register pending interactions on the root to avoid losing traced interaction data.18            schedulePendingInteractions(root, expirationTime); // This is a legacy edge case. The initial mount of a ReactDOM.render-ed19            // root inside of batchedUpdates should be synchronous, but layout updates20            // should be deferred until the end of the batch....1-2__scheduleUpdateOnFiber.js
Source:1-2__scheduleUpdateOnFiber.js  
2    checkForNestedUpdates();3    warnAboutInvalidUpdatesOnClassComponentsInDEV(fiber);4    var root = markUpdateTimeFromFiberToRoot(fiber, expirationTime);5    if (root === null) {6        warnAboutUpdateOnUnmountedFiberInDEV(fiber);7        return;8    }9    checkForInterruption(fiber, expirationTime);10    recordScheduleUpdate(); // TODO: computeExpirationForFiber also reads the priority. Pass the11    // priority as an argument to that function and this one.12    var priorityLevel = getCurrentPriorityLevel();13    if (expirationTime === Sync) {14        if ( // Check if we're inside unbatchedUpdates15            (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering16            (executionContext & (RenderContext | CommitContext)) === NoContext) {17            // Register pending interactions on the root to avoid losing traced interaction data.18            schedulePendingInteractions(root, expirationTime); // This is a legacy edge case. The initial mount of a ReactDOM.render-ed19            // root inside of batchedUpdates should be synchronous, but layout updates20            // should be deferred until the end of the batch....updateEqueue.js
Source:updateEqueue.js  
...49) {50  checkForNestedUpdates();51  const root = markUpdateTimeFromFiberToRoot(fiber, expirationTime);52  if (root === null) {53    warnAboutUpdateOnUnmountedFiberInDEV(fiber);54    return;55  }56  checkForInterruption(fiber, expirationTime);57  const priorityLevel = getCurrentPriorityLevel();58  if (expirationTime === Sync) {59    if (60      (executionContext & LegacyUnbatchedContext) !== NoContext &&61      (executionContext & (RenderContext | CommitContext)) === NoContext62    ) {63      schedulePendingInteractions(root, expirationTime);64      performSyncWorkOnRoot(root);65    } else {66      ensureRootIsScheduled(root);67      schedulePendingInteractions(root, expirationTime);...Using AI Code Generation
1const { chromium } = require('playwright');2const { warnAboutUpdateOnUnmountedFiberInDEV } = require('react-dom/cjs/react-dom.development');3(async () => {4  const browser = await chromium.launch();5  const context = await browser.newContext();6  const page = await context.newPage();7  await page.click('text=Counter');8  await page.click('text=Toggle');9  await page.click('text=Toggle');10  const result = warnAboutUpdateOnUnmountedFiberInDEV('Counter');11  console.log(result);12  await browser.close();13})();14        throw new Error(`Request Interception is not enabled!`);15await page.route('**/*', route => {16  route.continue();17});18await page.route('**/*', route => {19  route.continue();20});21await page.route('**/*', route => {22  route.continue();23});24await page.route('**/*', route => {25  route.continue();26});27await page.route('**/*', route => {28  route.continue();29});30Your name to display (optional):31Your name to display (optional):32await page.route('**/*', route => {33  route.continue();34});35Your name to display (optional):36Your name to display (optional):37await page.route('**/*', route => {38  route.continue();39});Using AI Code Generation
1const { warnAboutUpdateOnUnmountedFiberInDEV } = require('playwright/lib/internal');2const { chromium } = require('playwright');3(async () => {4  const browser = await chromium.launch();5  const context = await browser.newContext();6  const page = await context.newPage();7  await page.click('text=Get Started');8  await page.click('text=Docs');9  await page.click('text=API');10  await page.click('text=class: Page');11  await page.click('text=method: Page.click');12  await page.click('text=Examples');13  await page.click('text=Click a button');14  await page.click('text=Run');15  await page.click('text=Close');16  await page.click('text=class: Page');17  await page.click('text=method: Page.click');18  await page.click('text=Examples');19  await page.click('text=Click a button');20  await page.click('text=Run');21  await page.click('text=Close');22  await page.click('text=class: Page');23  await page.click('text=method: Page.click');24  await page.click('text=Examples');25  await page.click('text=Click a button');26  await page.click('text=Run');27  await page.click('text=Close');28  await page.click('text=class: Page');29  await page.click('text=method: Page.click');30  await page.click('text=Examples');31  await page.click('text=Click a button');32  await page.click('text=Run');33  await page.click('text=Close');34  await page.click('text=class: Page');35  await page.click('text=method: Page.click');36  await page.click('text=Examples');37  await page.click('text=Click a button');38  await page.click('text=Run');39  await page.click('text=Close');40  await page.click('text=class: Page');41  await page.click('text=method: Page.click');42  await page.click('text=Examples');43  await page.click('text=Click a button');44  await page.click('text=Run');45  await page.click('text=Close');46  await page.click('text=class: Page');47  await page.click('text=method: Page.click');48  await page.click('text=ExamplesUsing AI Code Generation
1const { warnAboutUpdateOnUnmountedFiberInDEV } = require('@playwright/test/lib/server/inspector/inspector');2const { test } = require('@playwright/test');3test('should warn about update on unmounted Fiber', async ({ page }) => {4  await page.evaluate(() => {5    warnAboutUpdateOnUnmountedFiberInDEV();6  });7});8const { test } = require('@playwright/test');9test('should log all messages sent from the extension to the browser console', async ({ page }) => {10  await page.context().addInitScript(() => {11    window.addEventListener('message', (event) => {12      if (event.source === window && event.data && event.data.source === 'react-devtools-content-script') {13        console.log('Received message from the extension:', event.data);14      }15    });16  });17});18Received message from the extension: {source: "react-devtools-content-script", type: "ping"}19const { test } = require('@playwright/test');20test('should send the "ping" message to the extension', asyncUsing AI Code Generation
1const { warnAboutUpdateOnUnmountedFiberInDEV } = require('@playwright/test/lib/server/trace/recorder/recorder');2warnAboutUpdateOnUnmountedFiberInDEV('test');3const { warnAboutUpdateOnUnmountedFiberInDEV } = require('@playwright/test/lib/server/trace/recorder/recorder');4warnAboutUpdateOnUnmountedFiberInDEV('test');5const { warnAboutUpdateOnUnmountedFiberInDEV } = require('@playwright/test/lib/server/trace/recorder/recorder');6warnAboutUpdateOnUnmountedFiberInDEV('test');7const { warnAboutUpdateOnUnmountedFiberInDEV } = require('@playwright/test/lib/server/trace/recorder/recorder');8warnAboutUpdateOnUnmountedFiberInDEV('test');9const { warnAboutUpdateOnUnmountedFiberInDEV } = require('@playwright/test/lib/server/trace/recorder/recorder');10warnAboutUpdateOnUnmountedFiberInDEV('test');11const { warnAboutUpdateOnUnmountedFiberInDEV } = require('@playwright/test/lib/server/trace/recorder/recorder');12warnAboutUpdateOnUnmountedFiberInDEV('test');Using AI Code Generation
1const { warnAboutUpdateOnUnmountedFiberInDEV } = require( 'react-dom/cjs/react-dom.development' );2warnAboutUpdateOnUnmountedFiberInDEV( 'test' );3module.exports = {4  use: {5    viewport: { width: 1200, height: 1000 },6  },7    {8      use: {9      },10    },11};12      at reportException (node_modules/playwright-core/lib/cjs/pw-runner/workerRunner.js:262:15)13      at runNextTicks (node_modules/process/next_tick.js:52:3)14      at processImmediate (node_modules/process/next_tick.js:63:5)15      at warnAboutUpdateOnUnmountedFiberInDEV (node_modules/react-dom/cjs/react-dom.development.js:23269:19)16      at scheduleUpdateOnFiber (node_modules/react-dom/cjs/react-dom.development.js:21261:7)17      at Object.enqueueSetState (node_modules/react-dom/cjs/react-dom.development.js:12934:5)18      at test.setState (node_modules/react/cjs/react.development.js:325:16)19      at warnAboutUpdateOnUnmountedFiberInDEV (node_modules/react-dom/cjs/react-dom.development.js:23269:19)20      at scheduleUpdateOnFiber (node_modules/react-dom/cjs/react-dom.development.js:21261:7)21      at Object.enqueueSetState (node_modules/react-dom/cjs/react-dom.development.js:12934:Using AI Code Generation
1const { warnAboutUpdateOnUnmountedFiberInDEV } = require('playwright/lib/server/webkit/wk');2warnAboutUpdateOnUnmountedFiberInDEV();3const { warnAboutStringRefsInDEV } = require('playwright/lib/server/webkit/wk');4warnAboutStringRefsInDEV();5const { warnAboutDeprecatedLifecycles } = require('playwright/lib/server/webkit/wk');6warnAboutDeprecatedLifecycles();7const { warnAboutDefaultPropsOnFunctionComponents } = require('playwright/lib/server/webkit/wk');8warnAboutDefaultPropsOnFunctionComponents();9const { warnAboutDirectlyAssigningPropsToState } = require('playwright/lib/server/webkit/wk');10warnAboutDirectlyAssigningPropsToState();11const { warnAboutContextTypeOnFunctionComponent } = require('playwright/lib/server/webkit/wk');12warnAboutContextTypeOnFunctionComponent();13const { warnAboutDeprecatedFindDOMNode } = require('playwright/lib/server/webkit/wk');14warnAboutDeprecatedFindDOMNode();15const { warnAboutShorthandPropertyCollision } = require('playwright/lib/server/webkit/wk');16warnAboutShorthandPropertyCollision();17const { warnAboutFunctionRefs } = require('playwright/lib/server/webkit/wk');18warnAboutFunctionRefs();19const { warnAboutReassigningProps } = require('playwright/lib/server/webkit/wk');Using AI Code Generation
1const { warnAboutUpdateOnUnmountedFiberInDEV } = require('playwright/lib/server/trace/recorder/recorderTraceEvents');2warnAboutUpdateOnUnmountedFiberInDEV();3import { test } from '@playwright/test';4test('example test', async ({ page }) => {5});6import { test } from '@playwright/test';7test('example test', async ({ page }) => {8});9import { test } from '@playwright/test';10test('example test', async ({ page }) => {11});12import { test } from '@playwright/test';13test('example test', async ({ page }) => {14});15import { test } from '@playwright/test';16test('example test', async ({ page }) => {17});18import { test } from '@playwright/test';19test('example test', async ({ page }) => {20});21import { test } from '@playwright/test';22test('example test', async ({ page }) => {23});24import { test } from '@playwright/test';25test('example test', async ({ page }) => {26});27import { test } from '@playwright/test';28test('example test', async ({ page }) => {29});LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!
