Best JavaScript code snippet using playwright-internal
ReactFiberWorkLoop.old.js
Source:ReactFiberWorkLoop.old.js  
...491      didScheduleUpdateDuringPassiveEffects = true;492    }493  }494  // Mark that the root has a pending update.495  markRootUpdated(root, lane, eventTime);496  if (497    (executionContext & RenderContext) !== NoLanes &&498    root === workInProgressRoot499  ) {500    // This update was dispatched during the render phase. This is a mistake501    // if the update originates from user space (with the exception of local502    // hook updates, which are handled differently and don't reach this503    // function), but there are some internal React features that use this as504    // an implementation detail, like selective hydration.505    warnAboutRenderPhaseUpdatesInDEV(fiber);506    // Track lanes that were updated during the render phase507    workInProgressRootRenderPhaseUpdatedLanes = mergeLanes(508      workInProgressRootRenderPhaseUpdatedLanes,509      lane,510    );511  } else {512    // This is a normal update, scheduled from outside the render phase. For513    // example, during an input event.514    if (enableUpdaterTracking) {515      if (isDevToolsPresent) {516        addFiberToLanesMap(root, fiber, lane);517      }518    }519    warnIfUpdatesNotWrappedWithActDEV(fiber);520    if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {521      if (522        (executionContext & CommitContext) !== NoContext &&523        root === rootCommittingMutationOrLayoutEffects524      ) {525        if (fiber.mode & ProfileMode) {526          let current = fiber;527          while (current !== null) {528            if (current.tag === Profiler) {529              const {id, onNestedUpdateScheduled} = current.memoizedProps;530              if (typeof onNestedUpdateScheduled === 'function') {531                onNestedUpdateScheduled(id);532              }533            }534            current = current.return;535          }536        }537      }538    }539    if (enableTransitionTracing) {540      const transition = ReactCurrentBatchConfig.transition;541      if (transition !== null) {542        if (transition.startTime === -1) {543          transition.startTime = now();544        }545        addTransitionToLanesMap(root, transition, lane);546      }547    }548    if (root === workInProgressRoot) {549      // TODO: Consolidate with `isInterleavedUpdate` check550      // Received an update to a tree that's in the middle of rendering. Mark551      // that there was an interleaved update work on this root. Unless the552      // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render553      // phase update. In that case, we don't treat render phase updates as if554      // they were interleaved, for backwards compat reasons.555      if (556        deferRenderPhaseUpdateToNextBatch ||557        (executionContext & RenderContext) === NoContext558      ) {559        workInProgressRootInterleavedUpdatedLanes = mergeLanes(560          workInProgressRootInterleavedUpdatedLanes,561          lane,562        );563      }564      if (workInProgressRootExitStatus === RootSuspendedWithDelay) {565        // The root already suspended with a delay, which means this render566        // definitely won't finish. Since we have a new update, let's mark it as567        // suspended now, right before marking the incoming update. This has the568        // effect of interrupting the current render and switching to the update.569        // TODO: Make sure this doesn't override pings that happen while we've570        // already started rendering.571        markRootSuspended(root, workInProgressRootRenderLanes);572      }573    }574    ensureRootIsScheduled(root, eventTime);575    if (576      lane === SyncLane &&577      executionContext === NoContext &&578      (fiber.mode & ConcurrentMode) === NoMode &&579      // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.580      !(__DEV__ && ReactCurrentActQueue.isBatchingLegacy)581    ) {582      // Flush the synchronous work now, unless we're already working or inside583      // a batch. This is intentionally inside scheduleUpdateOnFiber instead of584      // scheduleCallbackForFiber to preserve the ability to schedule a callback585      // without immediately flushing it. We only do this for user-initiated586      // updates, to preserve historical behavior of legacy mode.587      resetRenderTimer();588      flushSyncCallbacksOnlyInLegacyMode();589    }590  }591  return root;592}593export function scheduleInitialHydrationOnRoot(594  root: FiberRoot,595  lane: Lane,596  eventTime: number,597) {598  // This is a special fork of scheduleUpdateOnFiber that is only used to599  // schedule the initial hydration of a root that has just been created. Most600  // of the stuff in scheduleUpdateOnFiber can be skipped.601  //602  // The main reason for this separate path, though, is to distinguish the603  // initial children from subsequent updates. In fully client-rendered roots604  // (createRoot instead of hydrateRoot), all top-level renders are modeled as605  // updates, but hydration roots are special because the initial render must606  // match what was rendered on the server.607  const current = root.current;608  current.lanes = lane;609  markRootUpdated(root, lane, eventTime);610  ensureRootIsScheduled(root, eventTime);611}612// This is split into a separate function so we can mark a fiber with pending613// work without treating it as a typical update that originates from an event;614// e.g. retrying a Suspense boundary isn't an update, but it does schedule work615// on a fiber.616function markUpdateLaneFromFiberToRoot(617  sourceFiber: Fiber,618  lane: Lane,619): FiberRoot | null {620  // Update the source fiber's lanes621  sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);622  let alternate = sourceFiber.alternate;623  if (alternate !== null) {624    alternate.lanes = mergeLanes(alternate.lanes, lane);625  }626  if (__DEV__) {627    if (628      alternate === null &&629      (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags630    ) {631      warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);632    }633  }634  // Walk the parent path to the root and update the child lanes.635  let node = sourceFiber;636  let parent = sourceFiber.return;637  while (parent !== null) {638    parent.childLanes = mergeLanes(parent.childLanes, lane);639    alternate = parent.alternate;640    if (alternate !== null) {641      alternate.childLanes = mergeLanes(alternate.childLanes, lane);642    } else {643      if (__DEV__) {644        if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {645          warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);646        }647      }648    }649    node = parent;650    parent = parent.return;651  }652  if (node.tag === HostRoot) {653    const root: FiberRoot = node.stateNode;654    return root;655  } else {656    return null;657  }658}659export function isInterleavedUpdate(fiber: Fiber, lane: Lane) {660  return (661    // TODO: Optimize slightly by comparing to root that fiber belongs to.662    // Requires some refactoring. Not a big deal though since it's rare for663    // concurrent apps to have more than a single root.664    workInProgressRoot !== null &&665    (fiber.mode & ConcurrentMode) !== NoMode &&666    // If this is a render phase update (i.e. UNSAFE_componentWillReceiveProps),667    // then don't treat this as an interleaved update. This pattern is668    // accompanied by a warning but we haven't fully deprecated it yet. We can669    // remove once the deferRenderPhaseUpdateToNextBatch flag is enabled.670    (deferRenderPhaseUpdateToNextBatch ||671      (executionContext & RenderContext) === NoContext)672  );673}674// Use this function to schedule a task for a root. There's only one task per675// root; if a task was already scheduled, we'll check to make sure the priority676// of the existing task is the same as the priority of the next level that the677// root has work on. This function is called on every update, and right before678// exiting a task.679function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {680  const existingCallbackNode = root.callbackNode;681  // Check if any lanes are being starved by other work. If so, mark them as682  // expired so we know to work on those next.683  markStarvedLanesAsExpired(root, currentTime);684  // Determine the next lanes to work on, and their priority.685  const nextLanes = getNextLanes(686    root,687    root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,688  );689  if (nextLanes === NoLanes) {690    // Special case: There's nothing to work on.691    if (existingCallbackNode !== null) {692      cancelCallback(existingCallbackNode);693    }694    root.callbackNode = null;695    root.callbackPriority = NoLane;696    return;697  }698  // We use the highest priority lane to represent the priority of the callback.699  const newCallbackPriority = getHighestPriorityLane(nextLanes);700  // Check if there's an existing task. We may be able to reuse it.701  const existingCallbackPriority = root.callbackPriority;702  if (703    existingCallbackPriority === newCallbackPriority &&704    // Special case related to `act`. If the currently scheduled task is a705    // Scheduler task, rather than an `act` task, cancel it and re-scheduled706    // on the `act` queue.707    !(708      __DEV__ &&709      ReactCurrentActQueue.current !== null &&710      existingCallbackNode !== fakeActCallbackNode711    )712  ) {713    if (__DEV__) {714      // If we're going to re-use an existing task, it needs to exist.715      // Assume that discrete update microtasks are non-cancellable and null.716      // TODO: Temporary until we confirm this warning is not fired.717      if (718        existingCallbackNode == null &&719        existingCallbackPriority !== SyncLane720      ) {721        console.error(722          'Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue.',723        );724      }725    }726    // The priority hasn't changed. We can reuse the existing task. Exit.727    return;728  }729  if (existingCallbackNode != null) {730    // Cancel the existing callback. We'll schedule a new one below.731    cancelCallback(existingCallbackNode);732  }733  // Schedule a new callback.734  let newCallbackNode;735  if (newCallbackPriority === SyncLane) {736    // Special case: Sync React callbacks are scheduled on a special737    // internal queue738    if (root.tag === LegacyRoot) {739      if (__DEV__ && ReactCurrentActQueue.isBatchingLegacy !== null) {740        ReactCurrentActQueue.didScheduleLegacyUpdate = true;741      }742      scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));743    } else {744      scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));745    }746    if (supportsMicrotasks) {747      // Flush the queue in a microtask.748      if (__DEV__ && ReactCurrentActQueue.current !== null) {749        // Inside `act`, use our internal `act` queue so that these get flushed750        // at the end of the current scope even when using the sync version751        // of `act`.752        ReactCurrentActQueue.current.push(flushSyncCallbacks);753      } else {754        scheduleMicrotask(() => {755          // In Safari, appending an iframe forces microtasks to run.756          // https://github.com/facebook/react/issues/22459757          // We don't support running callbacks in the middle of render758          // or commit so we need to check against that.759          if (executionContext === NoContext) {760            // It's only safe to do this conditionally because we always761            // check for pending work before we exit the task.762            flushSyncCallbacks();763          }764        });765      }766    } else {767      // Flush the queue in an Immediate task.768      scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks);769    }770    newCallbackNode = null;771  } else {772    let schedulerPriorityLevel;773    switch (lanesToEventPriority(nextLanes)) {774      case DiscreteEventPriority:775        schedulerPriorityLevel = ImmediateSchedulerPriority;776        break;777      case ContinuousEventPriority:778        schedulerPriorityLevel = UserBlockingSchedulerPriority;779        break;780      case DefaultEventPriority:781        schedulerPriorityLevel = NormalSchedulerPriority;782        break;783      case IdleEventPriority:784        schedulerPriorityLevel = IdleSchedulerPriority;785        break;786      default:787        schedulerPriorityLevel = NormalSchedulerPriority;788        break;789    }790    newCallbackNode = scheduleCallback(791      schedulerPriorityLevel,792      performConcurrentWorkOnRoot.bind(null, root),793    );794  }795  root.callbackPriority = newCallbackPriority;796  root.callbackNode = newCallbackNode;797}798// This is the entry point for every concurrent task, i.e. anything that799// goes through Scheduler.800function performConcurrentWorkOnRoot(root, didTimeout) {801  if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {802    resetNestedUpdateFlag();803  }804  // Since we know we're in a React event, we can clear the current805  // event time. The next update will compute a new event time.806  currentEventTime = NoTimestamp;807  currentEventTransitionLane = NoLanes;808  if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {809    throw new Error('Should not already be working.');810  }811  // Flush any pending passive effects before deciding which lanes to work on,812  // in case they schedule additional work.813  const originalCallbackNode = root.callbackNode;814  const didFlushPassiveEffects = flushPassiveEffects();815  if (didFlushPassiveEffects) {816    // Something in the passive effect phase may have canceled the current task.817    // Check if the task node for this root was changed.818    if (root.callbackNode !== originalCallbackNode) {819      // The current task was canceled. Exit. We don't need to call820      // `ensureRootIsScheduled` because the check above implies either that821      // there's a new task, or that there's no remaining work on this root.822      return null;823    } else {824      // Current task was not canceled. Continue.825    }826  }827  // Determine the next lanes to work on, using the fields stored828  // on the root.829  let lanes = getNextLanes(830    root,831    root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,832  );833  if (lanes === NoLanes) {834    // Defensive coding. This is never expected to happen.835    return null;836  }837  // We disable time-slicing in some cases: if the work has been CPU-bound838  // for too long ("expired" work, to prevent starvation), or we're in839  // sync-updates-by-default mode.840  // TODO: We only check `didTimeout` defensively, to account for a Scheduler841  // bug we're still investigating. Once the bug in Scheduler is fixed,842  // we can remove this, since we track expiration ourselves.843  const shouldTimeSlice =844    !includesBlockingLane(root, lanes) &&845    !includesExpiredLane(root, lanes) &&846    (disableSchedulerTimeoutInWorkLoop || !didTimeout);847  let exitStatus = shouldTimeSlice848    ? renderRootConcurrent(root, lanes)849    : renderRootSync(root, lanes);850  if (exitStatus !== RootInProgress) {851    if (exitStatus === RootErrored) {852      // If something threw an error, try rendering one more time. We'll853      // render synchronously to block concurrent data mutations, and we'll854      // includes all pending updates are included. If it still fails after855      // the second attempt, we'll give up and commit the resulting tree.856      const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);857      if (errorRetryLanes !== NoLanes) {858        lanes = errorRetryLanes;859        exitStatus = recoverFromConcurrentError(root, errorRetryLanes);860      }861    }862    if (exitStatus === RootFatalErrored) {863      const fatalError = workInProgressRootFatalError;864      prepareFreshStack(root, NoLanes);865      markRootSuspended(root, lanes);866      ensureRootIsScheduled(root, now());867      throw fatalError;868    }869    if (exitStatus === RootDidNotComplete) {870      // The render unwound without completing the tree. This happens in special871      // cases where need to exit the current render without producing a872      // consistent tree or committing.873      //874      // This should only happen during a concurrent render, not a discrete or875      // synchronous update. We should have already checked for this when we876      // unwound the stack.877      markRootSuspended(root, lanes);878    } else {879      // The render completed.880      // Check if this render may have yielded to a concurrent event, and if so,881      // confirm that any newly rendered stores are consistent.882      // TODO: It's possible that even a concurrent render may never have yielded883      // to the main thread, if it was fast enough, or if it expired. We could884      // skip the consistency check in that case, too.885      const renderWasConcurrent = !includesBlockingLane(root, lanes);886      const finishedWork: Fiber = (root.current.alternate: any);887      if (888        renderWasConcurrent &&889        !isRenderConsistentWithExternalStores(finishedWork)890      ) {891        // A store was mutated in an interleaved event. Render again,892        // synchronously, to block further mutations.893        exitStatus = renderRootSync(root, lanes);894        // We need to check again if something threw895        if (exitStatus === RootErrored) {896          const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);897          if (errorRetryLanes !== NoLanes) {898            lanes = errorRetryLanes;899            exitStatus = recoverFromConcurrentError(root, errorRetryLanes);900            // We assume the tree is now consistent because we didn't yield to any901            // concurrent events.902          }903        }904        if (exitStatus === RootFatalErrored) {905          const fatalError = workInProgressRootFatalError;906          prepareFreshStack(root, NoLanes);907          markRootSuspended(root, lanes);908          ensureRootIsScheduled(root, now());909          throw fatalError;910        }911      }912      // We now have a consistent tree. The next step is either to commit it,913      // or, if something suspended, wait to commit it after a timeout.914      root.finishedWork = finishedWork;915      root.finishedLanes = lanes;916      finishConcurrentRender(root, exitStatus, lanes);917    }918  }919  ensureRootIsScheduled(root, now());920  if (root.callbackNode === originalCallbackNode) {921    // The task node scheduled for this root is the same one that's922    // currently executed. Need to return a continuation.923    return performConcurrentWorkOnRoot.bind(null, root);924  }925  return null;926}927function recoverFromConcurrentError(root, errorRetryLanes) {928  // If an error occurred during hydration, discard server response and fall929  // back to client side render.930  // Before rendering again, save the errors from the previous attempt.931  const errorsFromFirstAttempt = workInProgressRootConcurrentErrors;932  if (isRootDehydrated(root)) {933    // The shell failed to hydrate. Set a flag to force a client rendering934    // during the next attempt. To do this, we call prepareFreshStack now935    // to create the root work-in-progress fiber. This is a bit weird in terms936    // of factoring, because it relies on renderRootSync not calling937    // prepareFreshStack again in the call below, which happens because the938    // root and lanes haven't changed.939    //940    // TODO: I think what we should do is set ForceClientRender inside941    // throwException, like we do for nested Suspense boundaries. The reason942    // it's here instead is so we can switch to the synchronous work loop, too.943    // Something to consider for a future refactor.944    const rootWorkInProgress = prepareFreshStack(root, errorRetryLanes);945    rootWorkInProgress.flags |= ForceClientRender;946    if (__DEV__) {947      errorHydratingContainer(root.containerInfo);948    }949  }950  const exitStatus = renderRootSync(root, errorRetryLanes);951  if (exitStatus !== RootErrored) {952    // Successfully finished rendering on retry953    // The errors from the failed first attempt have been recovered. Add954    // them to the collection of recoverable errors. We'll log them in the955    // commit phase.956    const errorsFromSecondAttempt = workInProgressRootRecoverableErrors;957    workInProgressRootRecoverableErrors = errorsFromFirstAttempt;958    // The errors from the second attempt should be queued after the errors959    // from the first attempt, to preserve the causal sequence.960    if (errorsFromSecondAttempt !== null) {961      queueRecoverableErrors(errorsFromSecondAttempt);962    }963  } else {964    // The UI failed to recover.965  }966  return exitStatus;967}968export function queueRecoverableErrors(errors: Array<mixed>) {969  if (workInProgressRootRecoverableErrors === null) {970    workInProgressRootRecoverableErrors = errors;971  } else {972    workInProgressRootRecoverableErrors.push.apply(973      workInProgressRootRecoverableErrors,974      errors,975    );976  }977}978function finishConcurrentRender(root, exitStatus, lanes) {979  switch (exitStatus) {980    case RootInProgress:981    case RootFatalErrored: {982      throw new Error('Root did not complete. This is a bug in React.');983    }984    // Flow knows about invariant, so it complains if I add a break985    // statement, but eslint doesn't know about invariant, so it complains986    // if I do. eslint-disable-next-line no-fallthrough987    case RootErrored: {988      // We should have already attempted to retry this tree. If we reached989      // this point, it errored again. Commit it.990      commitRoot(991        root,992        workInProgressRootRecoverableErrors,993        workInProgressTransitions,994      );995      break;996    }997    case RootSuspended: {998      markRootSuspended(root, lanes);999      // We have an acceptable loading state. We need to figure out if we1000      // should immediately commit it or wait a bit.1001      if (1002        includesOnlyRetries(lanes) &&1003        // do not delay if we're inside an act() scope1004        !shouldForceFlushFallbacksInDEV()1005      ) {1006        // This render only included retries, no updates. Throttle committing1007        // retries so that we don't show too many loading states too quickly.1008        const msUntilTimeout =1009          globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();1010        // Don't bother with a very short suspense time.1011        if (msUntilTimeout > 10) {1012          const nextLanes = getNextLanes(root, NoLanes);1013          if (nextLanes !== NoLanes) {1014            // There's additional work on this root.1015            break;1016          }1017          const suspendedLanes = root.suspendedLanes;1018          if (!isSubsetOfLanes(suspendedLanes, lanes)) {1019            // We should prefer to render the fallback of at the last1020            // suspended level. Ping the last suspended level to try1021            // rendering it again.1022            // FIXME: What if the suspended lanes are Idle? Should not restart.1023            const eventTime = requestEventTime();1024            markRootPinged(root, suspendedLanes, eventTime);1025            break;1026          }1027          // The render is suspended, it hasn't timed out, and there's no1028          // lower priority work to do. Instead of committing the fallback1029          // immediately, wait for more data to arrive.1030          root.timeoutHandle = scheduleTimeout(1031            commitRoot.bind(1032              null,1033              root,1034              workInProgressRootRecoverableErrors,1035              workInProgressTransitions,1036            ),1037            msUntilTimeout,1038          );1039          break;1040        }1041      }1042      // The work expired. Commit immediately.1043      commitRoot(1044        root,1045        workInProgressRootRecoverableErrors,1046        workInProgressTransitions,1047      );1048      break;1049    }1050    case RootSuspendedWithDelay: {1051      markRootSuspended(root, lanes);1052      if (includesOnlyNonUrgentLanes(lanes)) {1053        // This is a transition, so we should exit without committing a1054        // placeholder and without scheduling a timeout. Delay indefinitely1055        // until we receive more data.1056        break;1057      }1058      if (!shouldForceFlushFallbacksInDEV()) {1059        // This is not a transition, but we did trigger an avoided state.1060        // Schedule a placeholder to display after a short delay, using the Just1061        // Noticeable Difference.1062        // TODO: Is the JND optimization worth the added complexity? If this is1063        // the only reason we track the event time, then probably not.1064        // Consider removing.1065        const mostRecentEventTime = getMostRecentEventTime(root, lanes);1066        const eventTimeMs = mostRecentEventTime;1067        const timeElapsedMs = now() - eventTimeMs;1068        const msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs;1069        // Don't bother with a very short suspense time.1070        if (msUntilTimeout > 10) {1071          // Instead of committing the fallback immediately, wait for more data1072          // to arrive.1073          root.timeoutHandle = scheduleTimeout(1074            commitRoot.bind(1075              null,1076              root,1077              workInProgressRootRecoverableErrors,1078              workInProgressTransitions,1079            ),1080            msUntilTimeout,1081          );1082          break;1083        }1084      }1085      // Commit the placeholder.1086      commitRoot(1087        root,1088        workInProgressRootRecoverableErrors,1089        workInProgressTransitions,1090      );1091      break;1092    }1093    case RootCompleted: {1094      // The work completed. Ready to commit.1095      commitRoot(1096        root,1097        workInProgressRootRecoverableErrors,1098        workInProgressTransitions,1099      );1100      break;1101    }1102    default: {1103      throw new Error('Unknown root exit status.');1104    }1105  }1106}1107function isRenderConsistentWithExternalStores(finishedWork: Fiber): boolean {1108  // Search the rendered tree for external store reads, and check whether the1109  // stores were mutated in a concurrent event. Intentionally using an iterative1110  // loop instead of recursion so we can exit early.1111  let node: Fiber = finishedWork;1112  while (true) {1113    if (node.flags & StoreConsistency) {1114      const updateQueue: FunctionComponentUpdateQueue | null = (node.updateQueue: any);1115      if (updateQueue !== null) {1116        const checks = updateQueue.stores;1117        if (checks !== null) {1118          for (let i = 0; i < checks.length; i++) {1119            const check = checks[i];1120            const getSnapshot = check.getSnapshot;1121            const renderedValue = check.value;1122            try {1123              if (!is(getSnapshot(), renderedValue)) {1124                // Found an inconsistent store.1125                return false;1126              }1127            } catch (error) {1128              // If `getSnapshot` throws, return `false`. This will schedule1129              // a re-render, and the error will be rethrown during render.1130              return false;1131            }1132          }1133        }1134      }1135    }1136    const child = node.child;1137    if (node.subtreeFlags & StoreConsistency && child !== null) {1138      child.return = node;1139      node = child;1140      continue;1141    }1142    if (node === finishedWork) {1143      return true;1144    }1145    while (node.sibling === null) {1146      if (node.return === null || node.return === finishedWork) {1147        return true;1148      }1149      node = node.return;1150    }1151    node.sibling.return = node.return;1152    node = node.sibling;1153  }1154  // Flow doesn't know this is unreachable, but eslint does1155  // eslint-disable-next-line no-unreachable1156  return true;1157}1158function markRootSuspended(root, suspendedLanes) {1159  // When suspending, we should always exclude lanes that were pinged or (more1160  // rarely, since we try to avoid it) updated during the render phase.1161  // TODO: Lol maybe there's a better way to factor this besides this1162  // obnoxiously named function :)1163  suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);1164  suspendedLanes = removeLanes(1165    suspendedLanes,1166    workInProgressRootInterleavedUpdatedLanes,1167  );1168  markRootSuspended_dontCallThisOneDirectly(root, suspendedLanes);1169}1170// This is the entry point for synchronous tasks that don't go1171// through Scheduler1172function performSyncWorkOnRoot(root) {1173  if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {1174    syncNestedUpdateFlag();1175  }1176  if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {1177    throw new Error('Should not already be working.');1178  }1179  flushPassiveEffects();1180  let lanes = getNextLanes(root, NoLanes);1181  if (!includesSomeLane(lanes, SyncLane)) {1182    // There's no remaining sync work left.1183    ensureRootIsScheduled(root, now());1184    return null;1185  }1186  let exitStatus = renderRootSync(root, lanes);1187  if (root.tag !== LegacyRoot && exitStatus === RootErrored) {1188    // If something threw an error, try rendering one more time. We'll render1189    // synchronously to block concurrent data mutations, and we'll includes1190    // all pending updates are included. If it still fails after the second1191    // attempt, we'll give up and commit the resulting tree.1192    const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);1193    if (errorRetryLanes !== NoLanes) {1194      lanes = errorRetryLanes;1195      exitStatus = recoverFromConcurrentError(root, errorRetryLanes);1196    }1197  }1198  if (exitStatus === RootFatalErrored) {1199    const fatalError = workInProgressRootFatalError;1200    prepareFreshStack(root, NoLanes);1201    markRootSuspended(root, lanes);1202    ensureRootIsScheduled(root, now());1203    throw fatalError;1204  }1205  if (exitStatus === RootDidNotComplete) {1206    throw new Error('Root did not complete. This is a bug in React.');1207  }1208  // We now have a consistent tree. Because this is a sync render, we1209  // will commit it even if something suspended.1210  const finishedWork: Fiber = (root.current.alternate: any);1211  root.finishedWork = finishedWork;1212  root.finishedLanes = lanes;1213  commitRoot(1214    root,1215    workInProgressRootRecoverableErrors,1216    workInProgressTransitions,1217  );1218  // Before exiting, make sure there's a callback scheduled for the next1219  // pending level.1220  ensureRootIsScheduled(root, now());1221  return null;1222}1223export function flushRoot(root: FiberRoot, lanes: Lanes) {1224  if (lanes !== NoLanes) {1225    markRootEntangled(root, mergeLanes(lanes, SyncLane));1226    ensureRootIsScheduled(root, now());1227    if ((executionContext & (RenderContext | CommitContext)) === NoContext) {1228      resetRenderTimer();1229      flushSyncCallbacks();1230    }1231  }1232}1233export function getExecutionContext(): ExecutionContext {1234  return executionContext;1235}1236export function deferredUpdates<A>(fn: () => A): A {1237  const previousPriority = getCurrentUpdatePriority();1238  const prevTransition = ReactCurrentBatchConfig.transition;1239  try {1240    ReactCurrentBatchConfig.transition = null;1241    setCurrentUpdatePriority(DefaultEventPriority);1242    return fn();1243  } finally {1244    setCurrentUpdatePriority(previousPriority);1245    ReactCurrentBatchConfig.transition = prevTransition;1246  }1247}1248export function batchedUpdates<A, R>(fn: A => R, a: A): R {1249  const prevExecutionContext = executionContext;1250  executionContext |= BatchedContext;1251  try {1252    return fn(a);1253  } finally {1254    executionContext = prevExecutionContext;1255    // If there were legacy sync updates, flush them at the end of the outer1256    // most batchedUpdates-like method.1257    if (1258      executionContext === NoContext &&1259      // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.1260      !(__DEV__ && ReactCurrentActQueue.isBatchingLegacy)1261    ) {1262      resetRenderTimer();1263      flushSyncCallbacksOnlyInLegacyMode();1264    }1265  }1266}1267export function discreteUpdates<A, B, C, D, R>(1268  fn: (A, B, C, D) => R,1269  a: A,1270  b: B,1271  c: C,1272  d: D,1273): R {1274  const previousPriority = getCurrentUpdatePriority();1275  const prevTransition = ReactCurrentBatchConfig.transition;1276  try {1277    ReactCurrentBatchConfig.transition = null;1278    setCurrentUpdatePriority(DiscreteEventPriority);1279    return fn(a, b, c, d);1280  } finally {1281    setCurrentUpdatePriority(previousPriority);1282    ReactCurrentBatchConfig.transition = prevTransition;1283    if (executionContext === NoContext) {1284      resetRenderTimer();1285    }1286  }1287}1288// Overload the definition to the two valid signatures.1289// Warning, this opts-out of checking the function body.1290declare function flushSync<R>(fn: () => R): R;1291// eslint-disable-next-line no-redeclare1292declare function flushSync(): void;1293// eslint-disable-next-line no-redeclare1294export function flushSync(fn) {1295  // In legacy mode, we flush pending passive effects at the beginning of the1296  // next event, not at the end of the previous one.1297  if (1298    rootWithPendingPassiveEffects !== null &&1299    rootWithPendingPassiveEffects.tag === LegacyRoot &&1300    (executionContext & (RenderContext | CommitContext)) === NoContext1301  ) {1302    flushPassiveEffects();1303  }1304  const prevExecutionContext = executionContext;1305  executionContext |= BatchedContext;1306  const prevTransition = ReactCurrentBatchConfig.transition;1307  const previousPriority = getCurrentUpdatePriority();1308  try {1309    ReactCurrentBatchConfig.transition = null;1310    setCurrentUpdatePriority(DiscreteEventPriority);1311    if (fn) {1312      return fn();1313    } else {1314      return undefined;1315    }1316  } finally {1317    setCurrentUpdatePriority(previousPriority);1318    ReactCurrentBatchConfig.transition = prevTransition;1319    executionContext = prevExecutionContext;1320    // Flush the immediate callbacks that were scheduled during this batch.1321    // Note that this will happen even if batchedUpdates is higher up1322    // the stack.1323    if ((executionContext & (RenderContext | CommitContext)) === NoContext) {1324      flushSyncCallbacks();1325    }1326  }1327}1328export function isAlreadyRendering() {1329  // Used by the renderer to print a warning if certain APIs are called from1330  // the wrong context.1331  return (1332    __DEV__ &&1333    (executionContext & (RenderContext | CommitContext)) !== NoContext1334  );1335}1336export function flushControlled(fn: () => mixed): void {1337  const prevExecutionContext = executionContext;1338  executionContext |= BatchedContext;1339  const prevTransition = ReactCurrentBatchConfig.transition;1340  const previousPriority = getCurrentUpdatePriority();1341  try {1342    ReactCurrentBatchConfig.transition = null;1343    setCurrentUpdatePriority(DiscreteEventPriority);1344    fn();1345  } finally {1346    setCurrentUpdatePriority(previousPriority);1347    ReactCurrentBatchConfig.transition = prevTransition;1348    executionContext = prevExecutionContext;1349    if (executionContext === NoContext) {1350      // Flush the immediate callbacks that were scheduled during this batch1351      resetRenderTimer();1352      flushSyncCallbacks();1353    }1354  }1355}1356export function pushRenderLanes(fiber: Fiber, lanes: Lanes) {1357  pushToStack(subtreeRenderLanesCursor, subtreeRenderLanes, fiber);1358  subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);1359  workInProgressRootIncludedLanes = mergeLanes(1360    workInProgressRootIncludedLanes,1361    lanes,1362  );1363}1364export function popRenderLanes(fiber: Fiber) {1365  subtreeRenderLanes = subtreeRenderLanesCursor.current;1366  popFromStack(subtreeRenderLanesCursor, fiber);1367}1368function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber {1369  root.finishedWork = null;1370  root.finishedLanes = NoLanes;1371  const timeoutHandle = root.timeoutHandle;1372  if (timeoutHandle !== noTimeout) {1373    // The root previous suspended and scheduled a timeout to commit a fallback1374    // state. Now that we have additional work, cancel the timeout.1375    root.timeoutHandle = noTimeout;1376    // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above1377    cancelTimeout(timeoutHandle);1378  }1379  if (workInProgress !== null) {1380    let interruptedWork = workInProgress.return;1381    while (interruptedWork !== null) {1382      const current = interruptedWork.alternate;1383      unwindInterruptedWork(1384        current,1385        interruptedWork,1386        workInProgressRootRenderLanes,1387      );1388      interruptedWork = interruptedWork.return;1389    }1390  }1391  workInProgressRoot = root;1392  const rootWorkInProgress = createWorkInProgress(root.current, null);1393  workInProgress = rootWorkInProgress;1394  workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;1395  workInProgressRootExitStatus = RootInProgress;1396  workInProgressRootFatalError = null;1397  workInProgressRootSkippedLanes = NoLanes;1398  workInProgressRootInterleavedUpdatedLanes = NoLanes;1399  workInProgressRootRenderPhaseUpdatedLanes = NoLanes;1400  workInProgressRootPingedLanes = NoLanes;1401  workInProgressRootConcurrentErrors = null;1402  workInProgressRootRecoverableErrors = null;1403  enqueueInterleavedUpdates();1404  if (__DEV__) {1405    ReactStrictModeWarnings.discardPendingWarnings();1406  }1407  return rootWorkInProgress;1408}1409function handleError(root, thrownValue): void {1410  do {1411    let erroredWork = workInProgress;1412    try {1413      // Reset module-level state that was set during the render phase.1414      resetContextDependencies();1415      resetHooksAfterThrow();1416      resetCurrentDebugFiberInDEV();1417      // TODO: I found and added this missing line while investigating a1418      // separate issue. Write a regression test using string refs.1419      ReactCurrentOwner.current = null;1420      if (erroredWork === null || erroredWork.return === null) {1421        // Expected to be working on a non-root fiber. This is a fatal error1422        // because there's no ancestor that can handle it; the root is1423        // supposed to capture all errors that weren't caught by an error1424        // boundary.1425        workInProgressRootExitStatus = RootFatalErrored;1426        workInProgressRootFatalError = thrownValue;1427        // Set `workInProgress` to null. This represents advancing to the next1428        // sibling, or the parent if there are no siblings. But since the root1429        // has no siblings nor a parent, we set it to null. Usually this is1430        // handled by `completeUnitOfWork` or `unwindWork`, but since we're1431        // intentionally not calling those, we need set it here.1432        // TODO: Consider calling `unwindWork` to pop the contexts.1433        workInProgress = null;1434        return;1435      }1436      if (enableProfilerTimer && erroredWork.mode & ProfileMode) {1437        // Record the time spent rendering before an error was thrown. This1438        // avoids inaccurate Profiler durations in the case of a1439        // suspended render.1440        stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);1441      }1442      if (enableSchedulingProfiler) {1443        markComponentRenderStopped();1444        if (1445          thrownValue !== null &&1446          typeof thrownValue === 'object' &&1447          typeof thrownValue.then === 'function'1448        ) {1449          const wakeable: Wakeable = (thrownValue: any);1450          markComponentSuspended(1451            erroredWork,1452            wakeable,1453            workInProgressRootRenderLanes,1454          );1455        } else {1456          markComponentErrored(1457            erroredWork,1458            thrownValue,1459            workInProgressRootRenderLanes,1460          );1461        }1462      }1463      throwException(1464        root,1465        erroredWork.return,1466        erroredWork,1467        thrownValue,1468        workInProgressRootRenderLanes,1469      );1470      completeUnitOfWork(erroredWork);1471    } catch (yetAnotherThrownValue) {1472      // Something in the return path also threw.1473      thrownValue = yetAnotherThrownValue;1474      if (workInProgress === erroredWork && erroredWork !== null) {1475        // If this boundary has already errored, then we had trouble processing1476        // the error. Bubble it to the next boundary.1477        erroredWork = erroredWork.return;1478        workInProgress = erroredWork;1479      } else {1480        erroredWork = workInProgress;1481      }1482      continue;1483    }1484    // Return to the normal work loop.1485    return;1486  } while (true);1487}1488function pushDispatcher() {1489  const prevDispatcher = ReactCurrentDispatcher.current;1490  ReactCurrentDispatcher.current = ContextOnlyDispatcher;1491  if (prevDispatcher === null) {1492    // The React isomorphic package does not include a default dispatcher.1493    // Instead the first renderer will lazily attach one, in order to give1494    // nicer error messages.1495    return ContextOnlyDispatcher;1496  } else {1497    return prevDispatcher;1498  }1499}1500function popDispatcher(prevDispatcher) {1501  ReactCurrentDispatcher.current = prevDispatcher;1502}1503export function markCommitTimeOfFallback() {1504  globalMostRecentFallbackTime = now();1505}1506export function markSkippedUpdateLanes(lane: Lane | Lanes): void {1507  workInProgressRootSkippedLanes = mergeLanes(1508    lane,1509    workInProgressRootSkippedLanes,1510  );1511}1512export function renderDidSuspend(): void {1513  if (workInProgressRootExitStatus === RootInProgress) {1514    workInProgressRootExitStatus = RootSuspended;1515  }1516}1517export function renderDidSuspendDelayIfPossible(): void {1518  if (1519    workInProgressRootExitStatus === RootInProgress ||1520    workInProgressRootExitStatus === RootSuspended ||1521    workInProgressRootExitStatus === RootErrored1522  ) {1523    workInProgressRootExitStatus = RootSuspendedWithDelay;1524  }1525  // Check if there are updates that we skipped tree that might have unblocked1526  // this render.1527  if (1528    workInProgressRoot !== null &&1529    (includesNonIdleWork(workInProgressRootSkippedLanes) ||1530      includesNonIdleWork(workInProgressRootInterleavedUpdatedLanes))1531  ) {1532    // Mark the current render as suspended so that we switch to working on1533    // the updates that were skipped. Usually we only suspend at the end of1534    // the render phase.1535    // TODO: We should probably always mark the root as suspended immediately1536    // (inside this function), since by suspending at the end of the render1537    // phase introduces a potential mistake where we suspend lanes that were1538    // pinged or updated while we were rendering.1539    markRootSuspended(workInProgressRoot, workInProgressRootRenderLanes);1540  }1541}1542export function renderDidError(error: mixed) {1543  if (workInProgressRootExitStatus !== RootSuspendedWithDelay) {1544    workInProgressRootExitStatus = RootErrored;1545  }1546  if (workInProgressRootConcurrentErrors === null) {1547    workInProgressRootConcurrentErrors = [error];1548  } else {1549    workInProgressRootConcurrentErrors.push(error);1550  }1551}1552// Called during render to determine if anything has suspended.1553// Returns false if we're not sure.1554export function renderHasNotSuspendedYet(): boolean {1555  // If something errored or completed, we can't really be sure,1556  // so those are false.1557  return workInProgressRootExitStatus === RootInProgress;1558}1559function renderRootSync(root: FiberRoot, lanes: Lanes) {1560  const prevExecutionContext = executionContext;1561  executionContext |= RenderContext;1562  const prevDispatcher = pushDispatcher();1563  // If the root or lanes have changed, throw out the existing stack1564  // and prepare a fresh one. Otherwise we'll continue where we left off.1565  if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1566    if (enableUpdaterTracking) {1567      if (isDevToolsPresent) {1568        const memoizedUpdaters = root.memoizedUpdaters;1569        if (memoizedUpdaters.size > 0) {1570          restorePendingUpdaters(root, workInProgressRootRenderLanes);1571          memoizedUpdaters.clear();1572        }1573        // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.1574        // If we bailout on this work, we'll move them back (like above).1575        // It's important to move them now in case the work spawns more work at the same priority with different updaters.1576        // That way we can keep the current update and future updates separate.1577        movePendingFibersToMemoized(root, lanes);1578      }1579    }1580    workInProgressTransitions = getTransitionsForLanes(root, lanes);1581    prepareFreshStack(root, lanes);1582  }1583  if (__DEV__) {1584    if (enableDebugTracing) {1585      logRenderStarted(lanes);1586    }1587  }1588  if (enableSchedulingProfiler) {1589    markRenderStarted(lanes);1590  }1591  do {1592    try {1593      workLoopSync();1594      break;1595    } catch (thrownValue) {1596      handleError(root, thrownValue);1597    }1598  } while (true);1599  resetContextDependencies();1600  executionContext = prevExecutionContext;1601  popDispatcher(prevDispatcher);1602  if (workInProgress !== null) {1603    // This is a sync render, so we should have finished the whole tree.1604    throw new Error(1605      'Cannot commit an incomplete root. This error is likely caused by a ' +1606        'bug in React. Please file an issue.',1607    );1608  }1609  if (__DEV__) {1610    if (enableDebugTracing) {1611      logRenderStopped();1612    }1613  }1614  if (enableSchedulingProfiler) {1615    markRenderStopped();1616  }1617  // Set this to null to indicate there's no in-progress render.1618  workInProgressRoot = null;1619  workInProgressRootRenderLanes = NoLanes;1620  return workInProgressRootExitStatus;1621}1622// The work loop is an extremely hot path. Tell Closure not to inline it.1623/** @noinline */1624function workLoopSync() {1625  // Already timed out, so perform work without checking if we need to yield.1626  while (workInProgress !== null) {1627    performUnitOfWork(workInProgress);1628  }1629}1630function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {1631  const prevExecutionContext = executionContext;1632  executionContext |= RenderContext;1633  const prevDispatcher = pushDispatcher();1634  // If the root or lanes have changed, throw out the existing stack1635  // and prepare a fresh one. Otherwise we'll continue where we left off.1636  if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1637    if (enableUpdaterTracking) {1638      if (isDevToolsPresent) {1639        const memoizedUpdaters = root.memoizedUpdaters;1640        if (memoizedUpdaters.size > 0) {1641          restorePendingUpdaters(root, workInProgressRootRenderLanes);1642          memoizedUpdaters.clear();1643        }1644        // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.1645        // If we bailout on this work, we'll move them back (like above).1646        // It's important to move them now in case the work spawns more work at the same priority with different updaters.1647        // That way we can keep the current update and future updates separate.1648        movePendingFibersToMemoized(root, lanes);1649      }1650    }1651    workInProgressTransitions = getTransitionsForLanes(root, lanes);1652    resetRenderTimer();1653    prepareFreshStack(root, lanes);1654  }1655  if (__DEV__) {1656    if (enableDebugTracing) {1657      logRenderStarted(lanes);1658    }1659  }1660  if (enableSchedulingProfiler) {1661    markRenderStarted(lanes);1662  }1663  do {1664    try {1665      workLoopConcurrent();1666      break;1667    } catch (thrownValue) {1668      handleError(root, thrownValue);1669    }1670  } while (true);1671  resetContextDependencies();1672  popDispatcher(prevDispatcher);1673  executionContext = prevExecutionContext;1674  if (__DEV__) {1675    if (enableDebugTracing) {1676      logRenderStopped();1677    }1678  }1679  // Check if the tree has completed.1680  if (workInProgress !== null) {1681    // Still work remaining.1682    if (enableSchedulingProfiler) {1683      markRenderYielded();1684    }1685    return RootInProgress;1686  } else {1687    // Completed the tree.1688    if (enableSchedulingProfiler) {1689      markRenderStopped();1690    }1691    // Set this to null to indicate there's no in-progress render.1692    workInProgressRoot = null;1693    workInProgressRootRenderLanes = NoLanes;1694    // Return the final exit status.1695    return workInProgressRootExitStatus;1696  }1697}1698/** @noinline */1699function workLoopConcurrent() {1700  // Perform work until Scheduler asks us to yield1701  while (workInProgress !== null && !shouldYield()) {1702    performUnitOfWork(workInProgress);1703  }1704}1705function performUnitOfWork(unitOfWork: Fiber): void {1706  // The current, flushed, state of this fiber is the alternate. Ideally1707  // nothing should rely on this, but relying on it here means that we don't1708  // need an additional field on the work in progress.1709  const current = unitOfWork.alternate;1710  setCurrentDebugFiberInDEV(unitOfWork);1711  let next;1712  if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {1713    startProfilerTimer(unitOfWork);1714    next = beginWork(current, unitOfWork, subtreeRenderLanes);1715    stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);1716  } else {1717    next = beginWork(current, unitOfWork, subtreeRenderLanes);1718  }1719  resetCurrentDebugFiberInDEV();1720  unitOfWork.memoizedProps = unitOfWork.pendingProps;1721  if (next === null) {1722    // If this doesn't spawn new work, complete the current work.1723    completeUnitOfWork(unitOfWork);1724  } else {1725    workInProgress = next;1726  }1727  ReactCurrentOwner.current = null;1728}1729function completeUnitOfWork(unitOfWork: Fiber): void {1730  // Attempt to complete the current unit of work, then move to the next1731  // sibling. If there are no more siblings, return to the parent fiber.1732  let completedWork = unitOfWork;1733  do {1734    // The current, flushed, state of this fiber is the alternate. Ideally1735    // nothing should rely on this, but relying on it here means that we don't1736    // need an additional field on the work in progress.1737    const current = completedWork.alternate;1738    const returnFiber = completedWork.return;1739    // Check if the work completed or if something threw.1740    if ((completedWork.flags & Incomplete) === NoFlags) {1741      setCurrentDebugFiberInDEV(completedWork);1742      let next;1743      if (1744        !enableProfilerTimer ||1745        (completedWork.mode & ProfileMode) === NoMode1746      ) {1747        next = completeWork(current, completedWork, subtreeRenderLanes);1748      } else {1749        startProfilerTimer(completedWork);1750        next = completeWork(current, completedWork, subtreeRenderLanes);1751        // Update render duration assuming we didn't error.1752        stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1753      }1754      resetCurrentDebugFiberInDEV();1755      if (next !== null) {1756        // Completing this fiber spawned new work. Work on that next.1757        workInProgress = next;1758        return;1759      }1760    } else {1761      // This fiber did not complete because something threw. Pop values off1762      // the stack without entering the complete phase. If this is a boundary,1763      // capture values if possible.1764      const next = unwindWork(current, completedWork, subtreeRenderLanes);1765      // Because this fiber did not complete, don't reset its lanes.1766      if (next !== null) {1767        // If completing this work spawned new work, do that next. We'll come1768        // back here again.1769        // Since we're restarting, remove anything that is not a host effect1770        // from the effect tag.1771        next.flags &= HostEffectMask;1772        workInProgress = next;1773        return;1774      }1775      if (1776        enableProfilerTimer &&1777        (completedWork.mode & ProfileMode) !== NoMode1778      ) {1779        // Record the render duration for the fiber that errored.1780        stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1781        // Include the time spent working on failed children before continuing.1782        let actualDuration = completedWork.actualDuration;1783        let child = completedWork.child;1784        while (child !== null) {1785          actualDuration += child.actualDuration;1786          child = child.sibling;1787        }1788        completedWork.actualDuration = actualDuration;1789      }1790      if (returnFiber !== null) {1791        // Mark the parent fiber as incomplete and clear its subtree flags.1792        returnFiber.flags |= Incomplete;1793        returnFiber.subtreeFlags = NoFlags;1794        returnFiber.deletions = null;1795      } else {1796        // We've unwound all the way to the root.1797        workInProgressRootExitStatus = RootDidNotComplete;1798        workInProgress = null;1799        return;1800      }1801    }1802    const siblingFiber = completedWork.sibling;1803    if (siblingFiber !== null) {1804      // If there is more work to do in this returnFiber, do that next.1805      workInProgress = siblingFiber;1806      return;1807    }1808    // Otherwise, return to the parent1809    completedWork = returnFiber;1810    // Update the next thing we're working on in case something throws.1811    workInProgress = completedWork;1812  } while (completedWork !== null);1813  // We've reached the root.1814  if (workInProgressRootExitStatus === RootInProgress) {1815    workInProgressRootExitStatus = RootCompleted;1816  }1817}1818function commitRoot(1819  root: FiberRoot,1820  recoverableErrors: null | Array<mixed>,1821  transitions: Array<Transition> | null,1822) {1823  // TODO: This no longer makes any sense. We already wrap the mutation and1824  // layout phases. Should be able to remove.1825  const previousUpdateLanePriority = getCurrentUpdatePriority();1826  const prevTransition = ReactCurrentBatchConfig.transition;1827  try {1828    ReactCurrentBatchConfig.transition = null;1829    setCurrentUpdatePriority(DiscreteEventPriority);1830    commitRootImpl(1831      root,1832      recoverableErrors,1833      transitions,1834      previousUpdateLanePriority,1835    );1836  } finally {1837    ReactCurrentBatchConfig.transition = prevTransition;1838    setCurrentUpdatePriority(previousUpdateLanePriority);1839  }1840  return null;1841}1842function commitRootImpl(1843  root: FiberRoot,1844  recoverableErrors: null | Array<mixed>,1845  transitions: Array<Transition> | null,1846  renderPriorityLevel: EventPriority,1847) {1848  do {1849    // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which1850    // means `flushPassiveEffects` will sometimes result in additional1851    // passive effects. So we need to keep flushing in a loop until there are1852    // no more pending effects.1853    // TODO: Might be better if `flushPassiveEffects` did not automatically1854    // flush synchronous work at the end, to avoid factoring hazards like this.1855    flushPassiveEffects();1856  } while (rootWithPendingPassiveEffects !== null);1857  flushRenderPhaseStrictModeWarningsInDEV();1858  if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {1859    throw new Error('Should not already be working.');1860  }1861  const finishedWork = root.finishedWork;1862  const lanes = root.finishedLanes;1863  if (__DEV__) {1864    if (enableDebugTracing) {1865      logCommitStarted(lanes);1866    }1867  }1868  if (enableSchedulingProfiler) {1869    markCommitStarted(lanes);1870  }1871  if (finishedWork === null) {1872    if (__DEV__) {1873      if (enableDebugTracing) {1874        logCommitStopped();1875      }1876    }1877    if (enableSchedulingProfiler) {1878      markCommitStopped();1879    }1880    return null;1881  } else {1882    if (__DEV__) {1883      if (lanes === NoLanes) {1884        console.error(1885          'root.finishedLanes should not be empty during a commit. This is a ' +1886            'bug in React.',1887        );1888      }1889    }1890  }1891  root.finishedWork = null;1892  root.finishedLanes = NoLanes;1893  if (finishedWork === root.current) {1894    throw new Error(1895      'Cannot commit the same tree as before. This error is likely caused by ' +1896        'a bug in React. Please file an issue.',1897    );1898  }1899  // commitRoot never returns a continuation; it always finishes synchronously.1900  // So we can clear these now to allow a new callback to be scheduled.1901  root.callbackNode = null;1902  root.callbackPriority = NoLane;1903  // Update the first and last pending times on this root. The new first1904  // pending time is whatever is left on the root fiber.1905  let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);1906  markRootFinished(root, remainingLanes);1907  if (root === workInProgressRoot) {1908    // We can reset these now that they are finished.1909    workInProgressRoot = null;1910    workInProgress = null;1911    workInProgressRootRenderLanes = NoLanes;1912  } else {1913    // This indicates that the last root we worked on is not the same one that1914    // we're committing now. This most commonly happens when a suspended root1915    // times out.1916  }1917  // If there are pending passive effects, schedule a callback to process them.1918  // Do this as early as possible, so it is queued before anything else that1919  // might get scheduled in the commit phase. (See #16714.)1920  // TODO: Delete all other places that schedule the passive effect callback1921  // They're redundant.1922  if (1923    (finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||1924    (finishedWork.flags & PassiveMask) !== NoFlags1925  ) {1926    if (!rootDoesHavePassiveEffects) {1927      rootDoesHavePassiveEffects = true;1928      pendingPassiveEffectsRemainingLanes = remainingLanes;1929      // workInProgressTransitions might be overwritten, so we want1930      // to store it in pendingPassiveTransitions until they get processed1931      // We need to pass this through as an argument to commitRoot1932      // because workInProgressTransitions might have changed between1933      // the previous render and commit if we throttle the commit1934      // with setTimeout1935      pendingPassiveTransitions = transitions;1936      scheduleCallback(NormalSchedulerPriority, () => {1937        flushPassiveEffects();1938        // This render triggered passive effects: release the root cache pool1939        // *after* passive effects fire to avoid freeing a cache pool that may1940        // be referenced by a node in the tree (HostRoot, Cache boundary etc)1941        return null;1942      });1943    }1944  }1945  // Check if there are any effects in the whole tree.1946  // TODO: This is left over from the effect list implementation, where we had1947  // to check for the existence of `firstEffect` to satisfy Flow. I think the1948  // only other reason this optimization exists is because it affects profiling.1949  // Reconsider whether this is necessary.1950  const subtreeHasEffects =1951    (finishedWork.subtreeFlags &1952      (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1953    NoFlags;1954  const rootHasEffect =1955    (finishedWork.flags &1956      (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1957    NoFlags;1958  if (subtreeHasEffects || rootHasEffect) {1959    const prevTransition = ReactCurrentBatchConfig.transition;1960    ReactCurrentBatchConfig.transition = null;1961    const previousPriority = getCurrentUpdatePriority();1962    setCurrentUpdatePriority(DiscreteEventPriority);1963    const prevExecutionContext = executionContext;1964    executionContext |= CommitContext;1965    // Reset this to null before calling lifecycles1966    ReactCurrentOwner.current = null;1967    // The commit phase is broken into several sub-phases. We do a separate pass1968    // of the effect list for each phase: all mutation effects come before all1969    // layout effects, and so on.1970    // The first phase a "before mutation" phase. We use this phase to read the1971    // state of the host tree right before we mutate it. This is where1972    // getSnapshotBeforeUpdate is called.1973    const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(1974      root,1975      finishedWork,1976    );1977    if (enableProfilerTimer) {1978      // Mark the current commit time to be shared by all Profilers in this1979      // batch. This enables them to be grouped later.1980      recordCommitTime();1981    }1982    if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {1983      // Track the root here, rather than in commitLayoutEffects(), because of ref setters.1984      // Updates scheduled during ref detachment should also be flagged.1985      rootCommittingMutationOrLayoutEffects = root;1986    }1987    // The next phase is the mutation phase, where we mutate the host tree.1988    commitMutationEffects(root, finishedWork, lanes);1989    if (enableCreateEventHandleAPI) {1990      if (shouldFireAfterActiveInstanceBlur) {1991        afterActiveInstanceBlur();1992      }1993    }1994    resetAfterCommit(root.containerInfo);1995    // The work-in-progress tree is now the current tree. This must come after1996    // the mutation phase, so that the previous tree is still current during1997    // componentWillUnmount, but before the layout phase, so that the finished1998    // work is current during componentDidMount/Update.1999    root.current = finishedWork;2000    // The next phase is the layout phase, where we call effects that read2001    // the host tree after it's been mutated. The idiomatic use case for this is2002    // layout, but class component lifecycles also fire here for legacy reasons.2003    if (__DEV__) {2004      if (enableDebugTracing) {2005        logLayoutEffectsStarted(lanes);2006      }2007    }2008    if (enableSchedulingProfiler) {2009      markLayoutEffectsStarted(lanes);2010    }2011    commitLayoutEffects(finishedWork, root, lanes);2012    if (__DEV__) {2013      if (enableDebugTracing) {2014        logLayoutEffectsStopped();2015      }2016    }2017    if (enableSchedulingProfiler) {2018      markLayoutEffectsStopped();2019    }2020    if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {2021      rootCommittingMutationOrLayoutEffects = null;2022    }2023    // Tell Scheduler to yield at the end of the frame, so the browser has an2024    // opportunity to paint.2025    requestPaint();2026    executionContext = prevExecutionContext;2027    // Reset the priority to the previous non-sync value.2028    setCurrentUpdatePriority(previousPriority);2029    ReactCurrentBatchConfig.transition = prevTransition;2030  } else {2031    // No effects.2032    root.current = finishedWork;2033    // Measure these anyway so the flamegraph explicitly shows that there were2034    // no effects.2035    // TODO: Maybe there's a better way to report this.2036    if (enableProfilerTimer) {2037      recordCommitTime();2038    }2039  }2040  const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;2041  if (rootDoesHavePassiveEffects) {2042    // This commit has passive effects. Stash a reference to them. But don't2043    // schedule a callback until after flushing layout work.2044    rootDoesHavePassiveEffects = false;2045    rootWithPendingPassiveEffects = root;2046    pendingPassiveEffectsLanes = lanes;2047  } else {2048    // There were no passive effects, so we can immediately release the cache2049    // pool for this render.2050    releaseRootPooledCache(root, remainingLanes);2051    if (__DEV__) {2052      nestedPassiveUpdateCount = 0;2053      rootWithPassiveNestedUpdates = null;2054    }2055  }2056  // Read this again, since an effect might have updated it2057  remainingLanes = root.pendingLanes;2058  // Check if there's remaining work on this root2059  // TODO: This is part of the `componentDidCatch` implementation. Its purpose2060  // is to detect whether something might have called setState inside2061  // `componentDidCatch`. The mechanism is known to be flawed because `setState`2062  // inside `componentDidCatch` is itself flawed â that's why we recommend2063  // `getDerivedStateFromError` instead. However, it could be improved by2064  // checking if remainingLanes includes Sync work, instead of whether there's2065  // any work remaining at all (which would also include stuff like Suspense2066  // retries or transitions). It's been like this for a while, though, so fixing2067  // it probably isn't that urgent.2068  if (remainingLanes === NoLanes) {2069    // If there's no remaining work, we can clear the set of already failed2070    // error boundaries.2071    legacyErrorBoundariesThatAlreadyFailed = null;2072  }2073  if (__DEV__ && enableStrictEffects) {2074    if (!rootDidHavePassiveEffects) {2075      commitDoubleInvokeEffectsInDEV(root.current, false);2076    }2077  }2078  onCommitRootDevTools(finishedWork.stateNode, renderPriorityLevel);2079  if (enableUpdaterTracking) {2080    if (isDevToolsPresent) {2081      root.memoizedUpdaters.clear();2082    }2083  }2084  if (__DEV__) {2085    onCommitRootTestSelector();2086  }2087  // Always call this before exiting `commitRoot`, to ensure that any2088  // additional work on this root is scheduled.2089  ensureRootIsScheduled(root, now());2090  if (recoverableErrors !== null) {2091    // There were errors during this render, but recovered from them without2092    // needing to surface it to the UI. We log them here.2093    const onRecoverableError = root.onRecoverableError;2094    for (let i = 0; i < recoverableErrors.length; i++) {2095      const recoverableError = recoverableErrors[i];2096      onRecoverableError(recoverableError);2097    }2098  }2099  if (hasUncaughtError) {2100    hasUncaughtError = false;2101    const error = firstUncaughtError;2102    firstUncaughtError = null;2103    throw error;2104  }2105  // If the passive effects are the result of a discrete render, flush them2106  // synchronously at the end of the current task so that the result is2107  // immediately observable. Otherwise, we assume that they are not2108  // order-dependent and do not need to be observed by external systems, so we2109  // can wait until after paint.2110  // TODO: We can optimize this by not scheduling the callback earlier. Since we2111  // currently schedule the callback in multiple places, will wait until those2112  // are consolidated.2113  if (2114    includesSomeLane(pendingPassiveEffectsLanes, SyncLane) &&2115    root.tag !== LegacyRoot2116  ) {2117    flushPassiveEffects();2118  }2119  // Read this again, since a passive effect might have updated it2120  remainingLanes = root.pendingLanes;2121  if (includesSomeLane(remainingLanes, (SyncLane: Lane))) {2122    if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {2123      markNestedUpdateScheduled();2124    }2125    // Count the number of times the root synchronously re-renders without2126    // finishing. If there are too many, it indicates an infinite update loop.2127    if (root === rootWithNestedUpdates) {2128      nestedUpdateCount++;2129    } else {2130      nestedUpdateCount = 0;2131      rootWithNestedUpdates = root;2132    }2133  } else {2134    nestedUpdateCount = 0;2135  }2136  // If layout work was scheduled, flush it now.2137  flushSyncCallbacks();2138  if (__DEV__) {2139    if (enableDebugTracing) {2140      logCommitStopped();2141    }2142  }2143  if (enableSchedulingProfiler) {2144    markCommitStopped();2145  }2146  return null;2147}2148function releaseRootPooledCache(root: FiberRoot, remainingLanes: Lanes) {2149  if (enableCache) {2150    const pooledCacheLanes = (root.pooledCacheLanes &= remainingLanes);2151    if (pooledCacheLanes === NoLanes) {2152      // None of the remaining work relies on the cache pool. Clear it so2153      // subsequent requests get a new cache2154      const pooledCache = root.pooledCache;2155      if (pooledCache != null) {2156        root.pooledCache = null;2157        releaseCache(pooledCache);2158      }2159    }2160  }2161}2162export function flushPassiveEffects(): boolean {2163  // Returns whether passive effects were flushed.2164  // TODO: Combine this check with the one in flushPassiveEFfectsImpl. We should2165  // probably just combine the two functions. I believe they were only separate2166  // in the first place because we used to wrap it with2167  // `Scheduler.runWithPriority`, which accepts a function. But now we track the2168  // priority within React itself, so we can mutate the variable directly.2169  if (rootWithPendingPassiveEffects !== null) {2170    // Cache the root since rootWithPendingPassiveEffects is cleared in2171    // flushPassiveEffectsImpl2172    const root = rootWithPendingPassiveEffects;2173    // Cache and clear the remaining lanes flag; it must be reset since this2174    // method can be called from various places, not always from commitRoot2175    // where the remaining lanes are known2176    const remainingLanes = pendingPassiveEffectsRemainingLanes;2177    pendingPassiveEffectsRemainingLanes = NoLanes;2178    const renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes);2179    const priority = lowerEventPriority(DefaultEventPriority, renderPriority);2180    const prevTransition = ReactCurrentBatchConfig.transition;2181    const previousPriority = getCurrentUpdatePriority();2182    try {2183      ReactCurrentBatchConfig.transition = null;2184      setCurrentUpdatePriority(priority);2185      return flushPassiveEffectsImpl();2186    } finally {2187      setCurrentUpdatePriority(previousPriority);2188      ReactCurrentBatchConfig.transition = prevTransition;2189      // Once passive effects have run for the tree - giving components a2190      // chance to retain cache instances they use - release the pooled2191      // cache at the root (if there is one)2192      releaseRootPooledCache(root, remainingLanes);2193    }2194  }2195  return false;2196}2197export function enqueuePendingPassiveProfilerEffect(fiber: Fiber): void {2198  if (enableProfilerTimer && enableProfilerCommitHooks) {2199    pendingPassiveProfilerEffects.push(fiber);2200    if (!rootDoesHavePassiveEffects) {2201      rootDoesHavePassiveEffects = true;2202      scheduleCallback(NormalSchedulerPriority, () => {2203        flushPassiveEffects();2204        return null;2205      });2206    }2207  }2208}2209function flushPassiveEffectsImpl() {2210  if (rootWithPendingPassiveEffects === null) {2211    return false;2212  }2213  // Cache and clear the transitions flag2214  const transitions = pendingPassiveTransitions;2215  pendingPassiveTransitions = null;2216  const root = rootWithPendingPassiveEffects;2217  const lanes = pendingPassiveEffectsLanes;2218  rootWithPendingPassiveEffects = null;2219  // TODO: This is sometimes out of sync with rootWithPendingPassiveEffects.2220  // Figure out why and fix it. It's not causing any known issues (probably2221  // because it's only used for profiling), but it's a refactor hazard.2222  pendingPassiveEffectsLanes = NoLanes;2223  if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {2224    throw new Error('Cannot flush passive effects while already rendering.');2225  }2226  if (__DEV__) {2227    isFlushingPassiveEffects = true;2228    didScheduleUpdateDuringPassiveEffects = false;2229    if (enableDebugTracing) {2230      logPassiveEffectsStarted(lanes);2231    }2232  }2233  if (enableSchedulingProfiler) {2234    markPassiveEffectsStarted(lanes);2235  }2236  const prevExecutionContext = executionContext;2237  executionContext |= CommitContext;2238  commitPassiveUnmountEffects(root.current);2239  commitPassiveMountEffects(root, root.current, lanes, transitions);2240  // TODO: Move to commitPassiveMountEffects2241  if (enableProfilerTimer && enableProfilerCommitHooks) {2242    const profilerEffects = pendingPassiveProfilerEffects;2243    pendingPassiveProfilerEffects = [];2244    for (let i = 0; i < profilerEffects.length; i++) {2245      const fiber = ((profilerEffects[i]: any): Fiber);2246      commitPassiveEffectDurations(root, fiber);2247    }2248  }2249  if (__DEV__) {2250    if (enableDebugTracing) {2251      logPassiveEffectsStopped();2252    }2253  }2254  if (enableSchedulingProfiler) {2255    markPassiveEffectsStopped();2256  }2257  if (__DEV__ && enableStrictEffects) {2258    commitDoubleInvokeEffectsInDEV(root.current, true);2259  }2260  executionContext = prevExecutionContext;2261  flushSyncCallbacks();2262  if (enableTransitionTracing) {2263    const prevPendingTransitionCallbacks = currentPendingTransitionCallbacks;2264    const prevRootTransitionCallbacks = root.transitionCallbacks;2265    if (2266      prevPendingTransitionCallbacks !== null &&2267      prevRootTransitionCallbacks !== null2268    ) {2269      // TODO(luna) Refactor this code into the Host Config2270      // TODO(luna) The end time here is not necessarily accurate2271      // because passive effects could be called before paint2272      // (synchronously) or after paint (normally). We need2273      // to come up with a way to get the correct end time for both cases.2274      // One solution is in the host config, if the passive effects2275      // have not yet been run, make a call to flush the passive effects2276      // right after paint.2277      const endTime = now();2278      currentPendingTransitionCallbacks = null;2279      scheduleCallback(IdleSchedulerPriority, () =>2280        processTransitionCallbacks(2281          prevPendingTransitionCallbacks,2282          endTime,2283          prevRootTransitionCallbacks,2284        ),2285      );2286    }2287  }2288  if (__DEV__) {2289    // If additional passive effects were scheduled, increment a counter. If this2290    // exceeds the limit, we'll fire a warning.2291    if (didScheduleUpdateDuringPassiveEffects) {2292      if (root === rootWithPassiveNestedUpdates) {2293        nestedPassiveUpdateCount++;2294      } else {2295        nestedPassiveUpdateCount = 0;2296        rootWithPassiveNestedUpdates = root;2297      }2298    } else {2299      nestedPassiveUpdateCount = 0;2300    }2301    isFlushingPassiveEffects = false;2302    didScheduleUpdateDuringPassiveEffects = false;2303  }2304  // TODO: Move to commitPassiveMountEffects2305  onPostCommitRootDevTools(root);2306  if (enableProfilerTimer && enableProfilerCommitHooks) {2307    const stateNode = root.current.stateNode;2308    stateNode.effectDuration = 0;2309    stateNode.passiveEffectDuration = 0;2310  }2311  return true;2312}2313export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {2314  return (2315    legacyErrorBoundariesThatAlreadyFailed !== null &&2316    legacyErrorBoundariesThatAlreadyFailed.has(instance)2317  );2318}2319export function markLegacyErrorBoundaryAsFailed(instance: mixed) {2320  if (legacyErrorBoundariesThatAlreadyFailed === null) {2321    legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);2322  } else {2323    legacyErrorBoundariesThatAlreadyFailed.add(instance);2324  }2325}2326function prepareToThrowUncaughtError(error: mixed) {2327  if (!hasUncaughtError) {2328    hasUncaughtError = true;2329    firstUncaughtError = error;2330  }2331}2332export const onUncaughtError = prepareToThrowUncaughtError;2333function captureCommitPhaseErrorOnRoot(2334  rootFiber: Fiber,2335  sourceFiber: Fiber,2336  error: mixed,2337) {2338  const errorInfo = createCapturedValue(error, sourceFiber);2339  const update = createRootErrorUpdate(rootFiber, errorInfo, (SyncLane: Lane));2340  enqueueUpdate(rootFiber, update, (SyncLane: Lane));2341  const eventTime = requestEventTime();2342  const root = markUpdateLaneFromFiberToRoot(rootFiber, (SyncLane: Lane));2343  if (root !== null) {2344    markRootUpdated(root, SyncLane, eventTime);2345    ensureRootIsScheduled(root, eventTime);2346  }2347}2348export function captureCommitPhaseError(2349  sourceFiber: Fiber,2350  nearestMountedAncestor: Fiber | null,2351  error: mixed,2352) {2353  if (__DEV__) {2354    reportUncaughtErrorInDEV(error);2355    setIsRunningInsertionEffect(false);2356  }2357  if (sourceFiber.tag === HostRoot) {2358    // Error was thrown at the root. There is no parent, so the root2359    // itself should capture it.2360    captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);2361    return;2362  }2363  let fiber = null;2364  if (skipUnmountedBoundaries) {2365    fiber = nearestMountedAncestor;2366  } else {2367    fiber = sourceFiber.return;2368  }2369  while (fiber !== null) {2370    if (fiber.tag === HostRoot) {2371      captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);2372      return;2373    } else if (fiber.tag === ClassComponent) {2374      const ctor = fiber.type;2375      const instance = fiber.stateNode;2376      if (2377        typeof ctor.getDerivedStateFromError === 'function' ||2378        (typeof instance.componentDidCatch === 'function' &&2379          !isAlreadyFailedLegacyErrorBoundary(instance))2380      ) {2381        const errorInfo = createCapturedValue(error, sourceFiber);2382        const update = createClassErrorUpdate(2383          fiber,2384          errorInfo,2385          (SyncLane: Lane),2386        );2387        enqueueUpdate(fiber, update, (SyncLane: Lane));2388        const eventTime = requestEventTime();2389        const root = markUpdateLaneFromFiberToRoot(fiber, (SyncLane: Lane));2390        if (root !== null) {2391          markRootUpdated(root, SyncLane, eventTime);2392          ensureRootIsScheduled(root, eventTime);2393        }2394        return;2395      }2396    }2397    fiber = fiber.return;2398  }2399  if (__DEV__) {2400    // TODO: Until we re-land skipUnmountedBoundaries (see #20147), this warning2401    // will fire for errors that are thrown by destroy functions inside deleted2402    // trees. What it should instead do is propagate the error to the parent of2403    // the deleted tree. In the meantime, do not add this warning to the2404    // allowlist; this is only for our internal use.2405    console.error(2406      'Internal React error: Attempted to capture a commit phase error ' +2407        'inside a detached tree. This indicates a bug in React. Likely ' +2408        'causes include deleting the same fiber more than once, committing an ' +2409        'already-finished tree, or an inconsistent return pointer.\n\n' +2410        'Error message:\n\n%s',2411      error,2412    );2413  }2414}2415export function pingSuspendedRoot(2416  root: FiberRoot,2417  wakeable: Wakeable,2418  pingedLanes: Lanes,2419) {2420  const pingCache = root.pingCache;2421  if (pingCache !== null) {2422    // The wakeable resolved, so we no longer need to memoize, because it will2423    // never be thrown again.2424    pingCache.delete(wakeable);2425  }2426  const eventTime = requestEventTime();2427  markRootPinged(root, pingedLanes, eventTime);2428  warnIfSuspenseResolutionNotWrappedWithActDEV(root);2429  if (2430    workInProgressRoot === root &&2431    isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)2432  ) {2433    // Received a ping at the same priority level at which we're currently2434    // rendering. We might want to restart this render. This should mirror2435    // the logic of whether or not a root suspends once it completes.2436    // TODO: If we're rendering sync either due to Sync, Batched or expired,2437    // we should probably never restart.2438    // If we're suspended with delay, or if it's a retry, we'll always suspend2439    // so we can always restart.2440    if (2441      workInProgressRootExitStatus === RootSuspendedWithDelay ||2442      (workInProgressRootExitStatus === RootSuspended &&2443        includesOnlyRetries(workInProgressRootRenderLanes) &&2444        now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)2445    ) {2446      // Restart from the root.2447      prepareFreshStack(root, NoLanes);2448    } else {2449      // Even though we can't restart right now, we might get an2450      // opportunity later. So we mark this render as having a ping.2451      workInProgressRootPingedLanes = mergeLanes(2452        workInProgressRootPingedLanes,2453        pingedLanes,2454      );2455    }2456  }2457  ensureRootIsScheduled(root, eventTime);2458}2459function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {2460  // The boundary fiber (a Suspense component or SuspenseList component)2461  // previously was rendered in its fallback state. One of the promises that2462  // suspended it has resolved, which means at least part of the tree was2463  // likely unblocked. Try rendering again, at a new lanes.2464  if (retryLane === NoLane) {2465    // TODO: Assign this to `suspenseState.retryLane`? to avoid2466    // unnecessary entanglement?2467    retryLane = requestRetryLane(boundaryFiber);2468  }2469  // TODO: Special case idle priority?2470  const eventTime = requestEventTime();2471  const root = markUpdateLaneFromFiberToRoot(boundaryFiber, retryLane);2472  if (root !== null) {2473    markRootUpdated(root, retryLane, eventTime);2474    ensureRootIsScheduled(root, eventTime);2475  }2476}2477export function retryDehydratedSuspenseBoundary(boundaryFiber: Fiber) {2478  const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2479  let retryLane = NoLane;2480  if (suspenseState !== null) {2481    retryLane = suspenseState.retryLane;2482  }2483  retryTimedOutBoundary(boundaryFiber, retryLane);2484}2485export function resolveRetryWakeable(boundaryFiber: Fiber, wakeable: Wakeable) {2486  let retryLane = NoLane; // Default2487  let retryCache: WeakSet<Wakeable> | Set<Wakeable> | null;...ReactFiberWorkLoop.new.js
Source:ReactFiberWorkLoop.new.js  
...473  if (root === null) {474    return null;475  }476  // Mark that the root has a pending update.477  markRootUpdated(root, lane, eventTime);478  if (479    (executionContext & RenderContext) !== NoLanes &&480    root === workInProgressRoot481  ) {482    // This update was dispatched during the render phase. This is a mistake483    // if the update originates from user space (with the exception of local484    // hook updates, which are handled differently and don't reach this485    // function), but there are some internal React features that use this as486    // an implementation detail, like selective hydration.487    warnAboutRenderPhaseUpdatesInDEV(fiber);488    // Track lanes that were updated during the render phase489    workInProgressRootRenderPhaseUpdatedLanes = mergeLanes(490      workInProgressRootRenderPhaseUpdatedLanes,491      lane,492    );493  } else {494    // This is a normal update, scheduled from outside the render phase. For495    // example, during an input event.496    if (enableUpdaterTracking) {497      if (isDevToolsPresent) {498        addFiberToLanesMap(root, fiber, lane);499      }500    }501    warnIfUpdatesNotWrappedWithActDEV(fiber);502    if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {503      if (504        (executionContext & CommitContext) !== NoContext &&505        root === rootCommittingMutationOrLayoutEffects506      ) {507        if (fiber.mode & ProfileMode) {508          let current = fiber;509          while (current !== null) {510            if (current.tag === Profiler) {511              const {id, onNestedUpdateScheduled} = current.memoizedProps;512              if (typeof onNestedUpdateScheduled === 'function') {513                onNestedUpdateScheduled(id);514              }515            }516            current = current.return;517          }518        }519      }520    }521    if (enableTransitionTracing) {522      const transition = ReactCurrentBatchConfig.transition;523      if (transition !== null) {524        if (transition.startTime === -1) {525          transition.startTime = now();526        }527        addTransitionToLanesMap(root, transition, lane);528      }529    }530    if (root.isDehydrated && root.tag !== LegacyRoot) {531      // This root's shell hasn't hydrated yet. Revert to client rendering.532      if (workInProgressRoot === root) {533        // If this happened during an interleaved event, interrupt the534        // in-progress hydration. Theoretically, we could attempt to force a535        // synchronous hydration before switching to client rendering, but the536        // most common reason the shell hasn't hydrated yet is because it537        // suspended. So it's very likely to suspend again anyway. For538        // simplicity, we'll skip that atttempt and go straight to539        // client rendering.540        //541        // Another way to model this would be to give the initial hydration its542        // own special lane. However, it may not be worth adding a lane solely543        // for this purpose, so we'll wait until we find another use case before544        // adding it.545        //546        // TODO: Consider only interrupting hydration if the priority of the547        // update is higher than default.548        prepareFreshStack(root, NoLanes);549      }550      root.isDehydrated = false;551      const error = new Error(552        'This root received an early update, before anything was able ' +553          'hydrate. Switched the entire root to client rendering.',554      );555      const onRecoverableError = root.onRecoverableError;556      onRecoverableError(error);557    } else if (root === workInProgressRoot) {558      // TODO: Consolidate with `isInterleavedUpdate` check559      // Received an update to a tree that's in the middle of rendering. Mark560      // that there was an interleaved update work on this root. Unless the561      // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render562      // phase update. In that case, we don't treat render phase updates as if563      // they were interleaved, for backwards compat reasons.564      if (565        deferRenderPhaseUpdateToNextBatch ||566        (executionContext & RenderContext) === NoContext567      ) {568        workInProgressRootInterleavedUpdatedLanes = mergeLanes(569          workInProgressRootInterleavedUpdatedLanes,570          lane,571        );572      }573      if (workInProgressRootExitStatus === RootSuspendedWithDelay) {574        // The root already suspended with a delay, which means this render575        // definitely won't finish. Since we have a new update, let's mark it as576        // suspended now, right before marking the incoming update. This has the577        // effect of interrupting the current render and switching to the update.578        // TODO: Make sure this doesn't override pings that happen while we've579        // already started rendering.580        markRootSuspended(root, workInProgressRootRenderLanes);581      }582    }583    ensureRootIsScheduled(root, eventTime);584    if (585      lane === SyncLane &&586      executionContext === NoContext &&587      (fiber.mode & ConcurrentMode) === NoMode &&588      // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.589      !(__DEV__ && ReactCurrentActQueue.isBatchingLegacy)590    ) {591      // Flush the synchronous work now, unless we're already working or inside592      // a batch. This is intentionally inside scheduleUpdateOnFiber instead of593      // scheduleCallbackForFiber to preserve the ability to schedule a callback594      // without immediately flushing it. We only do this for user-initiated595      // updates, to preserve historical behavior of legacy mode.596      resetRenderTimer();597      flushSyncCallbacksOnlyInLegacyMode();598    }599  }600  return root;601}602export function scheduleInitialHydrationOnRoot(603  root: FiberRoot,604  lane: Lane,605  eventTime: number,606) {607  // This is a special fork of scheduleUpdateOnFiber that is only used to608  // schedule the initial hydration of a root that has just been created. Most609  // of the stuff in scheduleUpdateOnFiber can be skipped.610  //611  // The main reason for this separate path, though, is to distinguish the612  // initial children from subsequent updates. In fully client-rendered roots613  // (createRoot instead of hydrateRoot), all top-level renders are modeled as614  // updates, but hydration roots are special because the initial render must615  // match what was rendered on the server.616  const current = root.current;617  current.lanes = lane;618  markRootUpdated(root, lane, eventTime);619  ensureRootIsScheduled(root, eventTime);620}621// This is split into a separate function so we can mark a fiber with pending622// work without treating it as a typical update that originates from an event;623// e.g. retrying a Suspense boundary isn't an update, but it does schedule work624// on a fiber.625function markUpdateLaneFromFiberToRoot(626  sourceFiber: Fiber,627  lane: Lane,628): FiberRoot | null {629  // Update the source fiber's lanes630  sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);631  let alternate = sourceFiber.alternate;632  if (alternate !== null) {633    alternate.lanes = mergeLanes(alternate.lanes, lane);634  }635  if (__DEV__) {636    if (637      alternate === null &&638      (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags639    ) {640      warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);641    }642  }643  // Walk the parent path to the root and update the child lanes.644  let node = sourceFiber;645  let parent = sourceFiber.return;646  while (parent !== null) {647    parent.childLanes = mergeLanes(parent.childLanes, lane);648    alternate = parent.alternate;649    if (alternate !== null) {650      alternate.childLanes = mergeLanes(alternate.childLanes, lane);651    } else {652      if (__DEV__) {653        if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {654          warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);655        }656      }657    }658    node = parent;659    parent = parent.return;660  }661  if (node.tag === HostRoot) {662    const root: FiberRoot = node.stateNode;663    return root;664  } else {665    return null;666  }667}668export function isInterleavedUpdate(fiber: Fiber, lane: Lane) {669  return (670    // TODO: Optimize slightly by comparing to root that fiber belongs to.671    // Requires some refactoring. Not a big deal though since it's rare for672    // concurrent apps to have more than a single root.673    workInProgressRoot !== null &&674    (fiber.mode & ConcurrentMode) !== NoMode &&675    // If this is a render phase update (i.e. UNSAFE_componentWillReceiveProps),676    // then don't treat this as an interleaved update. This pattern is677    // accompanied by a warning but we haven't fully deprecated it yet. We can678    // remove once the deferRenderPhaseUpdateToNextBatch flag is enabled.679    (deferRenderPhaseUpdateToNextBatch ||680      (executionContext & RenderContext) === NoContext)681  );682}683// Use this function to schedule a task for a root. There's only one task per684// root; if a task was already scheduled, we'll check to make sure the priority685// of the existing task is the same as the priority of the next level that the686// root has work on. This function is called on every update, and right before687// exiting a task.688function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {689  const existingCallbackNode = root.callbackNode;690  // Check if any lanes are being starved by other work. If so, mark them as691  // expired so we know to work on those next.692  markStarvedLanesAsExpired(root, currentTime);693  // Determine the next lanes to work on, and their priority.694  const nextLanes = getNextLanes(695    root,696    root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,697  );698  if (nextLanes === NoLanes) {699    // Special case: There's nothing to work on.700    if (existingCallbackNode !== null) {701      cancelCallback(existingCallbackNode);702    }703    root.callbackNode = null;704    root.callbackPriority = NoLane;705    return;706  }707  // We use the highest priority lane to represent the priority of the callback.708  const newCallbackPriority = getHighestPriorityLane(nextLanes);709  // Check if there's an existing task. We may be able to reuse it.710  const existingCallbackPriority = root.callbackPriority;711  if (712    existingCallbackPriority === newCallbackPriority &&713    // Special case related to `act`. If the currently scheduled task is a714    // Scheduler task, rather than an `act` task, cancel it and re-scheduled715    // on the `act` queue.716    !(717      __DEV__ &&718      ReactCurrentActQueue.current !== null &&719      existingCallbackNode !== fakeActCallbackNode720    )721  ) {722    if (__DEV__) {723      // If we're going to re-use an existing task, it needs to exist.724      // Assume that discrete update microtasks are non-cancellable and null.725      // TODO: Temporary until we confirm this warning is not fired.726      if (727        existingCallbackNode == null &&728        existingCallbackPriority !== SyncLane729      ) {730        console.error(731          'Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue.',732        );733      }734    }735    // The priority hasn't changed. We can reuse the existing task. Exit.736    return;737  }738  if (existingCallbackNode != null) {739    // Cancel the existing callback. We'll schedule a new one below.740    cancelCallback(existingCallbackNode);741  }742  // Schedule a new callback.743  let newCallbackNode;744  if (newCallbackPriority === SyncLane) {745    // Special case: Sync React callbacks are scheduled on a special746    // internal queue747    if (root.tag === LegacyRoot) {748      if (__DEV__ && ReactCurrentActQueue.isBatchingLegacy !== null) {749        ReactCurrentActQueue.didScheduleLegacyUpdate = true;750      }751      scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));752    } else {753      scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));754    }755    if (supportsMicrotasks) {756      // Flush the queue in a microtask.757      if (__DEV__ && ReactCurrentActQueue.current !== null) {758        // Inside `act`, use our internal `act` queue so that these get flushed759        // at the end of the current scope even when using the sync version760        // of `act`.761        ReactCurrentActQueue.current.push(flushSyncCallbacks);762      } else {763        scheduleMicrotask(() => {764          // In Safari, appending an iframe forces microtasks to run.765          // https://github.com/facebook/react/issues/22459766          // We don't support running callbacks in the middle of render767          // or commit so we need to check against that.768          if (executionContext === NoContext) {769            // It's only safe to do this conditionally because we always770            // check for pending work before we exit the task.771            flushSyncCallbacks();772          }773        });774      }775    } else {776      // Flush the queue in an Immediate task.777      scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks);778    }779    newCallbackNode = null;780  } else {781    let schedulerPriorityLevel;782    switch (lanesToEventPriority(nextLanes)) {783      case DiscreteEventPriority:784        schedulerPriorityLevel = ImmediateSchedulerPriority;785        break;786      case ContinuousEventPriority:787        schedulerPriorityLevel = UserBlockingSchedulerPriority;788        break;789      case DefaultEventPriority:790        schedulerPriorityLevel = NormalSchedulerPriority;791        break;792      case IdleEventPriority:793        schedulerPriorityLevel = IdleSchedulerPriority;794        break;795      default:796        schedulerPriorityLevel = NormalSchedulerPriority;797        break;798    }799    newCallbackNode = scheduleCallback(800      schedulerPriorityLevel,801      performConcurrentWorkOnRoot.bind(null, root),802    );803  }804  root.callbackPriority = newCallbackPriority;805  root.callbackNode = newCallbackNode;806}807// This is the entry point for every concurrent task, i.e. anything that808// goes through Scheduler.809function performConcurrentWorkOnRoot(root, didTimeout) {810  if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {811    resetNestedUpdateFlag();812  }813  // Since we know we're in a React event, we can clear the current814  // event time. The next update will compute a new event time.815  currentEventTime = NoTimestamp;816  currentEventTransitionLane = NoLanes;817  if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {818    throw new Error('Should not already be working.');819  }820  // Flush any pending passive effects before deciding which lanes to work on,821  // in case they schedule additional work.822  const originalCallbackNode = root.callbackNode;823  const didFlushPassiveEffects = flushPassiveEffects();824  if (didFlushPassiveEffects) {825    // Something in the passive effect phase may have canceled the current task.826    // Check if the task node for this root was changed.827    if (root.callbackNode !== originalCallbackNode) {828      // The current task was canceled. Exit. We don't need to call829      // `ensureRootIsScheduled` because the check above implies either that830      // there's a new task, or that there's no remaining work on this root.831      return null;832    } else {833      // Current task was not canceled. Continue.834    }835  }836  // Determine the next lanes to work on, using the fields stored837  // on the root.838  let lanes = getNextLanes(839    root,840    root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,841  );842  if (lanes === NoLanes) {843    // Defensive coding. This is never expected to happen.844    return null;845  }846  // We disable time-slicing in some cases: if the work has been CPU-bound847  // for too long ("expired" work, to prevent starvation), or we're in848  // sync-updates-by-default mode.849  // TODO: We only check `didTimeout` defensively, to account for a Scheduler850  // bug we're still investigating. Once the bug in Scheduler is fixed,851  // we can remove this, since we track expiration ourselves.852  const shouldTimeSlice =853    !includesBlockingLane(root, lanes) &&854    !includesExpiredLane(root, lanes) &&855    (disableSchedulerTimeoutInWorkLoop || !didTimeout);856  let exitStatus = shouldTimeSlice857    ? renderRootConcurrent(root, lanes)858    : renderRootSync(root, lanes);859  if (exitStatus !== RootInProgress) {860    if (exitStatus === RootErrored) {861      // If something threw an error, try rendering one more time. We'll862      // render synchronously to block concurrent data mutations, and we'll863      // includes all pending updates are included. If it still fails after864      // the second attempt, we'll give up and commit the resulting tree.865      const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);866      if (errorRetryLanes !== NoLanes) {867        lanes = errorRetryLanes;868        exitStatus = recoverFromConcurrentError(root, errorRetryLanes);869      }870    }871    if (exitStatus === RootFatalErrored) {872      const fatalError = workInProgressRootFatalError;873      prepareFreshStack(root, NoLanes);874      markRootSuspended(root, lanes);875      ensureRootIsScheduled(root, now());876      throw fatalError;877    }878    if (exitStatus === RootDidNotComplete) {879      // The render unwound without completing the tree. This happens in special880      // cases where need to exit the current render without producing a881      // consistent tree or committing.882      //883      // This should only happen during a concurrent render, not a discrete or884      // synchronous update. We should have already checked for this when we885      // unwound the stack.886      markRootSuspended(root, lanes);887    } else {888      // The render completed.889      // Check if this render may have yielded to a concurrent event, and if so,890      // confirm that any newly rendered stores are consistent.891      // TODO: It's possible that even a concurrent render may never have yielded892      // to the main thread, if it was fast enough, or if it expired. We could893      // skip the consistency check in that case, too.894      const renderWasConcurrent = !includesBlockingLane(root, lanes);895      const finishedWork: Fiber = (root.current.alternate: any);896      if (897        renderWasConcurrent &&898        !isRenderConsistentWithExternalStores(finishedWork)899      ) {900        // A store was mutated in an interleaved event. Render again,901        // synchronously, to block further mutations.902        exitStatus = renderRootSync(root, lanes);903        // We need to check again if something threw904        if (exitStatus === RootErrored) {905          const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);906          if (errorRetryLanes !== NoLanes) {907            lanes = errorRetryLanes;908            exitStatus = recoverFromConcurrentError(root, errorRetryLanes);909            // We assume the tree is now consistent because we didn't yield to any910            // concurrent events.911          }912        }913        if (exitStatus === RootFatalErrored) {914          const fatalError = workInProgressRootFatalError;915          prepareFreshStack(root, NoLanes);916          markRootSuspended(root, lanes);917          ensureRootIsScheduled(root, now());918          throw fatalError;919        }920      }921      // We now have a consistent tree. The next step is either to commit it,922      // or, if something suspended, wait to commit it after a timeout.923      root.finishedWork = finishedWork;924      root.finishedLanes = lanes;925      finishConcurrentRender(root, exitStatus, lanes);926    }927  }928  ensureRootIsScheduled(root, now());929  if (root.callbackNode === originalCallbackNode) {930    // The task node scheduled for this root is the same one that's931    // currently executed. Need to return a continuation.932    return performConcurrentWorkOnRoot.bind(null, root);933  }934  return null;935}936function recoverFromConcurrentError(root, errorRetryLanes) {937  // If an error occurred during hydration, discard server response and fall938  // back to client side render.939  if (root.isDehydrated) {940    root.isDehydrated = false;941    if (__DEV__) {942      errorHydratingContainer(root.containerInfo);943    }944    const error = new Error(945      'There was an error while hydrating. Because the error happened outside ' +946        'of a Suspense boundary, the entire root will switch to ' +947        'client rendering.',948    );949    renderDidError(error);950  }951  const errorsFromFirstAttempt = workInProgressRootConcurrentErrors;952  const exitStatus = renderRootSync(root, errorRetryLanes);953  if (exitStatus !== RootErrored) {954    // Successfully finished rendering on retry955    if (errorsFromFirstAttempt !== null) {956      // The errors from the failed first attempt have been recovered. Add957      // them to the collection of recoverable errors. We'll log them in the958      // commit phase.959      queueRecoverableErrors(errorsFromFirstAttempt);960    }961  } else {962    // The UI failed to recover.963  }964  return exitStatus;965}966export function queueRecoverableErrors(errors: Array<mixed>) {967  if (workInProgressRootRecoverableErrors === null) {968    workInProgressRootRecoverableErrors = errors;969  } else {970    workInProgressRootRecoverableErrors.push.apply(971      workInProgressRootRecoverableErrors,972      errors,973    );974  }975}976function finishConcurrentRender(root, exitStatus, lanes) {977  switch (exitStatus) {978    case RootInProgress:979    case RootFatalErrored: {980      throw new Error('Root did not complete. This is a bug in React.');981    }982    // Flow knows about invariant, so it complains if I add a break983    // statement, but eslint doesn't know about invariant, so it complains984    // if I do. eslint-disable-next-line no-fallthrough985    case RootErrored: {986      // We should have already attempted to retry this tree. If we reached987      // this point, it errored again. Commit it.988      commitRoot(root, workInProgressRootRecoverableErrors);989      break;990    }991    case RootSuspended: {992      markRootSuspended(root, lanes);993      // We have an acceptable loading state. We need to figure out if we994      // should immediately commit it or wait a bit.995      if (996        includesOnlyRetries(lanes) &&997        // do not delay if we're inside an act() scope998        !shouldForceFlushFallbacksInDEV()999      ) {1000        // This render only included retries, no updates. Throttle committing1001        // retries so that we don't show too many loading states too quickly.1002        const msUntilTimeout =1003          globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();1004        // Don't bother with a very short suspense time.1005        if (msUntilTimeout > 10) {1006          const nextLanes = getNextLanes(root, NoLanes);1007          if (nextLanes !== NoLanes) {1008            // There's additional work on this root.1009            break;1010          }1011          const suspendedLanes = root.suspendedLanes;1012          if (!isSubsetOfLanes(suspendedLanes, lanes)) {1013            // We should prefer to render the fallback of at the last1014            // suspended level. Ping the last suspended level to try1015            // rendering it again.1016            // FIXME: What if the suspended lanes are Idle? Should not restart.1017            const eventTime = requestEventTime();1018            markRootPinged(root, suspendedLanes, eventTime);1019            break;1020          }1021          // The render is suspended, it hasn't timed out, and there's no1022          // lower priority work to do. Instead of committing the fallback1023          // immediately, wait for more data to arrive.1024          root.timeoutHandle = scheduleTimeout(1025            commitRoot.bind(null, root, workInProgressRootRecoverableErrors),1026            msUntilTimeout,1027          );1028          break;1029        }1030      }1031      // The work expired. Commit immediately.1032      commitRoot(root, workInProgressRootRecoverableErrors);1033      break;1034    }1035    case RootSuspendedWithDelay: {1036      markRootSuspended(root, lanes);1037      if (includesOnlyTransitions(lanes)) {1038        // This is a transition, so we should exit without committing a1039        // placeholder and without scheduling a timeout. Delay indefinitely1040        // until we receive more data.1041        break;1042      }1043      if (!shouldForceFlushFallbacksInDEV()) {1044        // This is not a transition, but we did trigger an avoided state.1045        // Schedule a placeholder to display after a short delay, using the Just1046        // Noticeable Difference.1047        // TODO: Is the JND optimization worth the added complexity? If this is1048        // the only reason we track the event time, then probably not.1049        // Consider removing.1050        const mostRecentEventTime = getMostRecentEventTime(root, lanes);1051        const eventTimeMs = mostRecentEventTime;1052        const timeElapsedMs = now() - eventTimeMs;1053        const msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs;1054        // Don't bother with a very short suspense time.1055        if (msUntilTimeout > 10) {1056          // Instead of committing the fallback immediately, wait for more data1057          // to arrive.1058          root.timeoutHandle = scheduleTimeout(1059            commitRoot.bind(null, root, workInProgressRootRecoverableErrors),1060            msUntilTimeout,1061          );1062          break;1063        }1064      }1065      // Commit the placeholder.1066      commitRoot(root, workInProgressRootRecoverableErrors);1067      break;1068    }1069    case RootCompleted: {1070      // The work completed. Ready to commit.1071      commitRoot(root, workInProgressRootRecoverableErrors);1072      break;1073    }1074    default: {1075      throw new Error('Unknown root exit status.');1076    }1077  }1078}1079function isRenderConsistentWithExternalStores(finishedWork: Fiber): boolean {1080  // Search the rendered tree for external store reads, and check whether the1081  // stores were mutated in a concurrent event. Intentionally using an iterative1082  // loop instead of recursion so we can exit early.1083  let node: Fiber = finishedWork;1084  while (true) {1085    if (node.flags & StoreConsistency) {1086      const updateQueue: FunctionComponentUpdateQueue | null = (node.updateQueue: any);1087      if (updateQueue !== null) {1088        const checks = updateQueue.stores;1089        if (checks !== null) {1090          for (let i = 0; i < checks.length; i++) {1091            const check = checks[i];1092            const getSnapshot = check.getSnapshot;1093            const renderedValue = check.value;1094            try {1095              if (!is(getSnapshot(), renderedValue)) {1096                // Found an inconsistent store.1097                return false;1098              }1099            } catch (error) {1100              // If `getSnapshot` throws, return `false`. This will schedule1101              // a re-render, and the error will be rethrown during render.1102              return false;1103            }1104          }1105        }1106      }1107    }1108    const child = node.child;1109    if (node.subtreeFlags & StoreConsistency && child !== null) {1110      child.return = node;1111      node = child;1112      continue;1113    }1114    if (node === finishedWork) {1115      return true;1116    }1117    while (node.sibling === null) {1118      if (node.return === null || node.return === finishedWork) {1119        return true;1120      }1121      node = node.return;1122    }1123    node.sibling.return = node.return;1124    node = node.sibling;1125  }1126  // Flow doesn't know this is unreachable, but eslint does1127  // eslint-disable-next-line no-unreachable1128  return true;1129}1130function markRootSuspended(root, suspendedLanes) {1131  // When suspending, we should always exclude lanes that were pinged or (more1132  // rarely, since we try to avoid it) updated during the render phase.1133  // TODO: Lol maybe there's a better way to factor this besides this1134  // obnoxiously named function :)1135  suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);1136  suspendedLanes = removeLanes(1137    suspendedLanes,1138    workInProgressRootInterleavedUpdatedLanes,1139  );1140  markRootSuspended_dontCallThisOneDirectly(root, suspendedLanes);1141}1142// This is the entry point for synchronous tasks that don't go1143// through Scheduler1144function performSyncWorkOnRoot(root) {1145  if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {1146    syncNestedUpdateFlag();1147  }1148  if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {1149    throw new Error('Should not already be working.');1150  }1151  flushPassiveEffects();1152  let lanes = getNextLanes(root, NoLanes);1153  if (!includesSomeLane(lanes, SyncLane)) {1154    // There's no remaining sync work left.1155    ensureRootIsScheduled(root, now());1156    return null;1157  }1158  let exitStatus = renderRootSync(root, lanes);1159  if (root.tag !== LegacyRoot && exitStatus === RootErrored) {1160    // If something threw an error, try rendering one more time. We'll render1161    // synchronously to block concurrent data mutations, and we'll includes1162    // all pending updates are included. If it still fails after the second1163    // attempt, we'll give up and commit the resulting tree.1164    const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);1165    if (errorRetryLanes !== NoLanes) {1166      lanes = errorRetryLanes;1167      exitStatus = recoverFromConcurrentError(root, errorRetryLanes);1168    }1169  }1170  if (exitStatus === RootFatalErrored) {1171    const fatalError = workInProgressRootFatalError;1172    prepareFreshStack(root, NoLanes);1173    markRootSuspended(root, lanes);1174    ensureRootIsScheduled(root, now());1175    throw fatalError;1176  }1177  if (exitStatus === RootDidNotComplete) {1178    throw new Error('Root did not complete. This is a bug in React.');1179  }1180  // We now have a consistent tree. Because this is a sync render, we1181  // will commit it even if something suspended.1182  const finishedWork: Fiber = (root.current.alternate: any);1183  root.finishedWork = finishedWork;1184  root.finishedLanes = lanes;1185  commitRoot(root, workInProgressRootRecoverableErrors);1186  // Before exiting, make sure there's a callback scheduled for the next1187  // pending level.1188  ensureRootIsScheduled(root, now());1189  return null;1190}1191export function flushRoot(root: FiberRoot, lanes: Lanes) {1192  if (lanes !== NoLanes) {1193    markRootEntangled(root, mergeLanes(lanes, SyncLane));1194    ensureRootIsScheduled(root, now());1195    if ((executionContext & (RenderContext | CommitContext)) === NoContext) {1196      resetRenderTimer();1197      flushSyncCallbacks();1198    }1199  }1200}1201export function getExecutionContext(): ExecutionContext {1202  return executionContext;1203}1204export function deferredUpdates<A>(fn: () => A): A {1205  const previousPriority = getCurrentUpdatePriority();1206  const prevTransition = ReactCurrentBatchConfig.transition;1207  try {1208    ReactCurrentBatchConfig.transition = null;1209    setCurrentUpdatePriority(DefaultEventPriority);1210    return fn();1211  } finally {1212    setCurrentUpdatePriority(previousPriority);1213    ReactCurrentBatchConfig.transition = prevTransition;1214  }1215}1216export function batchedUpdates<A, R>(fn: A => R, a: A): R {1217  const prevExecutionContext = executionContext;1218  executionContext |= BatchedContext;1219  try {1220    return fn(a);1221  } finally {1222    executionContext = prevExecutionContext;1223    // If there were legacy sync updates, flush them at the end of the outer1224    // most batchedUpdates-like method.1225    if (1226      executionContext === NoContext &&1227      // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.1228      !(__DEV__ && ReactCurrentActQueue.isBatchingLegacy)1229    ) {1230      resetRenderTimer();1231      flushSyncCallbacksOnlyInLegacyMode();1232    }1233  }1234}1235export function discreteUpdates<A, B, C, D, R>(1236  fn: (A, B, C, D) => R,1237  a: A,1238  b: B,1239  c: C,1240  d: D,1241): R {1242  const previousPriority = getCurrentUpdatePriority();1243  const prevTransition = ReactCurrentBatchConfig.transition;1244  try {1245    ReactCurrentBatchConfig.transition = null;1246    setCurrentUpdatePriority(DiscreteEventPriority);1247    return fn(a, b, c, d);1248  } finally {1249    setCurrentUpdatePriority(previousPriority);1250    ReactCurrentBatchConfig.transition = prevTransition;1251    if (executionContext === NoContext) {1252      resetRenderTimer();1253    }1254  }1255}1256// Overload the definition to the two valid signatures.1257// Warning, this opts-out of checking the function body.1258declare function flushSync<R>(fn: () => R): R;1259// eslint-disable-next-line no-redeclare1260declare function flushSync(): void;1261// eslint-disable-next-line no-redeclare1262export function flushSync(fn) {1263  // In legacy mode, we flush pending passive effects at the beginning of the1264  // next event, not at the end of the previous one.1265  if (1266    rootWithPendingPassiveEffects !== null &&1267    rootWithPendingPassiveEffects.tag === LegacyRoot &&1268    (executionContext & (RenderContext | CommitContext)) === NoContext1269  ) {1270    flushPassiveEffects();1271  }1272  const prevExecutionContext = executionContext;1273  executionContext |= BatchedContext;1274  const prevTransition = ReactCurrentBatchConfig.transition;1275  const previousPriority = getCurrentUpdatePriority();1276  try {1277    ReactCurrentBatchConfig.transition = null;1278    setCurrentUpdatePriority(DiscreteEventPriority);1279    if (fn) {1280      return fn();1281    } else {1282      return undefined;1283    }1284  } finally {1285    setCurrentUpdatePriority(previousPriority);1286    ReactCurrentBatchConfig.transition = prevTransition;1287    executionContext = prevExecutionContext;1288    // Flush the immediate callbacks that were scheduled during this batch.1289    // Note that this will happen even if batchedUpdates is higher up1290    // the stack.1291    if ((executionContext & (RenderContext | CommitContext)) === NoContext) {1292      flushSyncCallbacks();1293    }1294  }1295}1296export function isAlreadyRendering() {1297  // Used by the renderer to print a warning if certain APIs are called from1298  // the wrong context.1299  return (1300    __DEV__ &&1301    (executionContext & (RenderContext | CommitContext)) !== NoContext1302  );1303}1304export function flushControlled(fn: () => mixed): void {1305  const prevExecutionContext = executionContext;1306  executionContext |= BatchedContext;1307  const prevTransition = ReactCurrentBatchConfig.transition;1308  const previousPriority = getCurrentUpdatePriority();1309  try {1310    ReactCurrentBatchConfig.transition = null;1311    setCurrentUpdatePriority(DiscreteEventPriority);1312    fn();1313  } finally {1314    setCurrentUpdatePriority(previousPriority);1315    ReactCurrentBatchConfig.transition = prevTransition;1316    executionContext = prevExecutionContext;1317    if (executionContext === NoContext) {1318      // Flush the immediate callbacks that were scheduled during this batch1319      resetRenderTimer();1320      flushSyncCallbacks();1321    }1322  }1323}1324export function pushRenderLanes(fiber: Fiber, lanes: Lanes) {1325  pushToStack(subtreeRenderLanesCursor, subtreeRenderLanes, fiber);1326  subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);1327  workInProgressRootIncludedLanes = mergeLanes(1328    workInProgressRootIncludedLanes,1329    lanes,1330  );1331}1332export function popRenderLanes(fiber: Fiber) {1333  subtreeRenderLanes = subtreeRenderLanesCursor.current;1334  popFromStack(subtreeRenderLanesCursor, fiber);1335}1336function prepareFreshStack(root: FiberRoot, lanes: Lanes) {1337  root.finishedWork = null;1338  root.finishedLanes = NoLanes;1339  const timeoutHandle = root.timeoutHandle;1340  if (timeoutHandle !== noTimeout) {1341    // The root previous suspended and scheduled a timeout to commit a fallback1342    // state. Now that we have additional work, cancel the timeout.1343    root.timeoutHandle = noTimeout;1344    // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above1345    cancelTimeout(timeoutHandle);1346  }1347  if (workInProgress !== null) {1348    let interruptedWork = workInProgress.return;1349    while (interruptedWork !== null) {1350      const current = interruptedWork.alternate;1351      unwindInterruptedWork(1352        current,1353        interruptedWork,1354        workInProgressRootRenderLanes,1355      );1356      interruptedWork = interruptedWork.return;1357    }1358  }1359  workInProgressRoot = root;1360  workInProgress = createWorkInProgress(root.current, null);1361  workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;1362  workInProgressRootExitStatus = RootInProgress;1363  workInProgressRootFatalError = null;1364  workInProgressRootSkippedLanes = NoLanes;1365  workInProgressRootInterleavedUpdatedLanes = NoLanes;1366  workInProgressRootRenderPhaseUpdatedLanes = NoLanes;1367  workInProgressRootPingedLanes = NoLanes;1368  workInProgressRootConcurrentErrors = null;1369  workInProgressRootRecoverableErrors = null;1370  enqueueInterleavedUpdates();1371  if (__DEV__) {1372    ReactStrictModeWarnings.discardPendingWarnings();1373  }1374}1375function handleError(root, thrownValue): void {1376  do {1377    let erroredWork = workInProgress;1378    try {1379      // Reset module-level state that was set during the render phase.1380      resetContextDependencies();1381      resetHooksAfterThrow();1382      resetCurrentDebugFiberInDEV();1383      // TODO: I found and added this missing line while investigating a1384      // separate issue. Write a regression test using string refs.1385      ReactCurrentOwner.current = null;1386      if (erroredWork === null || erroredWork.return === null) {1387        // Expected to be working on a non-root fiber. This is a fatal error1388        // because there's no ancestor that can handle it; the root is1389        // supposed to capture all errors that weren't caught by an error1390        // boundary.1391        workInProgressRootExitStatus = RootFatalErrored;1392        workInProgressRootFatalError = thrownValue;1393        // Set `workInProgress` to null. This represents advancing to the next1394        // sibling, or the parent if there are no siblings. But since the root1395        // has no siblings nor a parent, we set it to null. Usually this is1396        // handled by `completeUnitOfWork` or `unwindWork`, but since we're1397        // intentionally not calling those, we need set it here.1398        // TODO: Consider calling `unwindWork` to pop the contexts.1399        workInProgress = null;1400        return;1401      }1402      if (enableProfilerTimer && erroredWork.mode & ProfileMode) {1403        // Record the time spent rendering before an error was thrown. This1404        // avoids inaccurate Profiler durations in the case of a1405        // suspended render.1406        stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);1407      }1408      if (enableSchedulingProfiler) {1409        markComponentRenderStopped();1410        if (1411          thrownValue !== null &&1412          typeof thrownValue === 'object' &&1413          typeof thrownValue.then === 'function'1414        ) {1415          const wakeable: Wakeable = (thrownValue: any);1416          markComponentSuspended(1417            erroredWork,1418            wakeable,1419            workInProgressRootRenderLanes,1420          );1421        } else {1422          markComponentErrored(1423            erroredWork,1424            thrownValue,1425            workInProgressRootRenderLanes,1426          );1427        }1428      }1429      throwException(1430        root,1431        erroredWork.return,1432        erroredWork,1433        thrownValue,1434        workInProgressRootRenderLanes,1435      );1436      completeUnitOfWork(erroredWork);1437    } catch (yetAnotherThrownValue) {1438      // Something in the return path also threw.1439      thrownValue = yetAnotherThrownValue;1440      if (workInProgress === erroredWork && erroredWork !== null) {1441        // If this boundary has already errored, then we had trouble processing1442        // the error. Bubble it to the next boundary.1443        erroredWork = erroredWork.return;1444        workInProgress = erroredWork;1445      } else {1446        erroredWork = workInProgress;1447      }1448      continue;1449    }1450    // Return to the normal work loop.1451    return;1452  } while (true);1453}1454function pushDispatcher() {1455  const prevDispatcher = ReactCurrentDispatcher.current;1456  ReactCurrentDispatcher.current = ContextOnlyDispatcher;1457  if (prevDispatcher === null) {1458    // The React isomorphic package does not include a default dispatcher.1459    // Instead the first renderer will lazily attach one, in order to give1460    // nicer error messages.1461    return ContextOnlyDispatcher;1462  } else {1463    return prevDispatcher;1464  }1465}1466function popDispatcher(prevDispatcher) {1467  ReactCurrentDispatcher.current = prevDispatcher;1468}1469export function markCommitTimeOfFallback() {1470  globalMostRecentFallbackTime = now();1471}1472export function markSkippedUpdateLanes(lane: Lane | Lanes): void {1473  workInProgressRootSkippedLanes = mergeLanes(1474    lane,1475    workInProgressRootSkippedLanes,1476  );1477}1478export function renderDidSuspend(): void {1479  if (workInProgressRootExitStatus === RootInProgress) {1480    workInProgressRootExitStatus = RootSuspended;1481  }1482}1483export function renderDidSuspendDelayIfPossible(): void {1484  if (1485    workInProgressRootExitStatus === RootInProgress ||1486    workInProgressRootExitStatus === RootSuspended ||1487    workInProgressRootExitStatus === RootErrored1488  ) {1489    workInProgressRootExitStatus = RootSuspendedWithDelay;1490  }1491  // Check if there are updates that we skipped tree that might have unblocked1492  // this render.1493  if (1494    workInProgressRoot !== null &&1495    (includesNonIdleWork(workInProgressRootSkippedLanes) ||1496      includesNonIdleWork(workInProgressRootInterleavedUpdatedLanes))1497  ) {1498    // Mark the current render as suspended so that we switch to working on1499    // the updates that were skipped. Usually we only suspend at the end of1500    // the render phase.1501    // TODO: We should probably always mark the root as suspended immediately1502    // (inside this function), since by suspending at the end of the render1503    // phase introduces a potential mistake where we suspend lanes that were1504    // pinged or updated while we were rendering.1505    markRootSuspended(workInProgressRoot, workInProgressRootRenderLanes);1506  }1507}1508export function renderDidError(error: mixed) {1509  if (workInProgressRootExitStatus !== RootSuspendedWithDelay) {1510    workInProgressRootExitStatus = RootErrored;1511  }1512  if (workInProgressRootConcurrentErrors === null) {1513    workInProgressRootConcurrentErrors = [error];1514  } else {1515    workInProgressRootConcurrentErrors.push(error);1516  }1517}1518// Called during render to determine if anything has suspended.1519// Returns false if we're not sure.1520export function renderHasNotSuspendedYet(): boolean {1521  // If something errored or completed, we can't really be sure,1522  // so those are false.1523  return workInProgressRootExitStatus === RootInProgress;1524}1525function renderRootSync(root: FiberRoot, lanes: Lanes) {1526  const prevExecutionContext = executionContext;1527  executionContext |= RenderContext;1528  const prevDispatcher = pushDispatcher();1529  // If the root or lanes have changed, throw out the existing stack1530  // and prepare a fresh one. Otherwise we'll continue where we left off.1531  if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1532    if (enableUpdaterTracking) {1533      if (isDevToolsPresent) {1534        const memoizedUpdaters = root.memoizedUpdaters;1535        if (memoizedUpdaters.size > 0) {1536          restorePendingUpdaters(root, workInProgressRootRenderLanes);1537          memoizedUpdaters.clear();1538        }1539        // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.1540        // If we bailout on this work, we'll move them back (like above).1541        // It's important to move them now in case the work spawns more work at the same priority with different updaters.1542        // That way we can keep the current update and future updates separate.1543        movePendingFibersToMemoized(root, lanes);1544      }1545    }1546    workInProgressTransitions = getTransitionsForLanes(root, lanes);1547    prepareFreshStack(root, lanes);1548  }1549  if (__DEV__) {1550    if (enableDebugTracing) {1551      logRenderStarted(lanes);1552    }1553  }1554  if (enableSchedulingProfiler) {1555    markRenderStarted(lanes);1556  }1557  do {1558    try {1559      workLoopSync();1560      break;1561    } catch (thrownValue) {1562      handleError(root, thrownValue);1563    }1564  } while (true);1565  resetContextDependencies();1566  executionContext = prevExecutionContext;1567  popDispatcher(prevDispatcher);1568  if (workInProgress !== null) {1569    // This is a sync render, so we should have finished the whole tree.1570    throw new Error(1571      'Cannot commit an incomplete root. This error is likely caused by a ' +1572        'bug in React. Please file an issue.',1573    );1574  }1575  if (__DEV__) {1576    if (enableDebugTracing) {1577      logRenderStopped();1578    }1579  }1580  if (enableSchedulingProfiler) {1581    markRenderStopped();1582  }1583  // Set this to null to indicate there's no in-progress render.1584  workInProgressRoot = null;1585  workInProgressRootRenderLanes = NoLanes;1586  return workInProgressRootExitStatus;1587}1588// The work loop is an extremely hot path. Tell Closure not to inline it.1589/** @noinline */1590function workLoopSync() {1591  // Already timed out, so perform work without checking if we need to yield.1592  while (workInProgress !== null) {1593    performUnitOfWork(workInProgress);1594  }1595}1596function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {1597  const prevExecutionContext = executionContext;1598  executionContext |= RenderContext;1599  const prevDispatcher = pushDispatcher();1600  // If the root or lanes have changed, throw out the existing stack1601  // and prepare a fresh one. Otherwise we'll continue where we left off.1602  if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1603    if (enableUpdaterTracking) {1604      if (isDevToolsPresent) {1605        const memoizedUpdaters = root.memoizedUpdaters;1606        if (memoizedUpdaters.size > 0) {1607          restorePendingUpdaters(root, workInProgressRootRenderLanes);1608          memoizedUpdaters.clear();1609        }1610        // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.1611        // If we bailout on this work, we'll move them back (like above).1612        // It's important to move them now in case the work spawns more work at the same priority with different updaters.1613        // That way we can keep the current update and future updates separate.1614        movePendingFibersToMemoized(root, lanes);1615      }1616    }1617    workInProgressTransitions = getTransitionsForLanes(root, lanes);1618    resetRenderTimer();1619    prepareFreshStack(root, lanes);1620  }1621  if (__DEV__) {1622    if (enableDebugTracing) {1623      logRenderStarted(lanes);1624    }1625  }1626  if (enableSchedulingProfiler) {1627    markRenderStarted(lanes);1628  }1629  do {1630    try {1631      workLoopConcurrent();1632      break;1633    } catch (thrownValue) {1634      handleError(root, thrownValue);1635    }1636  } while (true);1637  resetContextDependencies();1638  popDispatcher(prevDispatcher);1639  executionContext = prevExecutionContext;1640  if (__DEV__) {1641    if (enableDebugTracing) {1642      logRenderStopped();1643    }1644  }1645  // Check if the tree has completed.1646  if (workInProgress !== null) {1647    // Still work remaining.1648    if (enableSchedulingProfiler) {1649      markRenderYielded();1650    }1651    return RootInProgress;1652  } else {1653    // Completed the tree.1654    if (enableSchedulingProfiler) {1655      markRenderStopped();1656    }1657    // Set this to null to indicate there's no in-progress render.1658    workInProgressRoot = null;1659    workInProgressRootRenderLanes = NoLanes;1660    // Return the final exit status.1661    return workInProgressRootExitStatus;1662  }1663}1664/** @noinline */1665function workLoopConcurrent() {1666  // Perform work until Scheduler asks us to yield1667  while (workInProgress !== null && !shouldYield()) {1668    performUnitOfWork(workInProgress);1669  }1670}1671function performUnitOfWork(unitOfWork: Fiber): void {1672  // The current, flushed, state of this fiber is the alternate. Ideally1673  // nothing should rely on this, but relying on it here means that we don't1674  // need an additional field on the work in progress.1675  const current = unitOfWork.alternate;1676  setCurrentDebugFiberInDEV(unitOfWork);1677  let next;1678  if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {1679    startProfilerTimer(unitOfWork);1680    next = beginWork(current, unitOfWork, subtreeRenderLanes);1681    stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);1682  } else {1683    next = beginWork(current, unitOfWork, subtreeRenderLanes);1684  }1685  resetCurrentDebugFiberInDEV();1686  unitOfWork.memoizedProps = unitOfWork.pendingProps;1687  if (next === null) {1688    // If this doesn't spawn new work, complete the current work.1689    completeUnitOfWork(unitOfWork);1690  } else {1691    workInProgress = next;1692  }1693  ReactCurrentOwner.current = null;1694}1695function completeUnitOfWork(unitOfWork: Fiber): void {1696  // Attempt to complete the current unit of work, then move to the next1697  // sibling. If there are no more siblings, return to the parent fiber.1698  let completedWork = unitOfWork;1699  do {1700    // The current, flushed, state of this fiber is the alternate. Ideally1701    // nothing should rely on this, but relying on it here means that we don't1702    // need an additional field on the work in progress.1703    const current = completedWork.alternate;1704    const returnFiber = completedWork.return;1705    // Check if the work completed or if something threw.1706    if ((completedWork.flags & Incomplete) === NoFlags) {1707      setCurrentDebugFiberInDEV(completedWork);1708      let next;1709      if (1710        !enableProfilerTimer ||1711        (completedWork.mode & ProfileMode) === NoMode1712      ) {1713        next = completeWork(current, completedWork, subtreeRenderLanes);1714      } else {1715        startProfilerTimer(completedWork);1716        next = completeWork(current, completedWork, subtreeRenderLanes);1717        // Update render duration assuming we didn't error.1718        stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1719      }1720      resetCurrentDebugFiberInDEV();1721      if (next !== null) {1722        // Completing this fiber spawned new work. Work on that next.1723        workInProgress = next;1724        return;1725      }1726    } else {1727      // This fiber did not complete because something threw. Pop values off1728      // the stack without entering the complete phase. If this is a boundary,1729      // capture values if possible.1730      const next = unwindWork(current, completedWork, subtreeRenderLanes);1731      // Because this fiber did not complete, don't reset its lanes.1732      if (next !== null) {1733        // If completing this work spawned new work, do that next. We'll come1734        // back here again.1735        // Since we're restarting, remove anything that is not a host effect1736        // from the effect tag.1737        next.flags &= HostEffectMask;1738        workInProgress = next;1739        return;1740      }1741      if (1742        enableProfilerTimer &&1743        (completedWork.mode & ProfileMode) !== NoMode1744      ) {1745        // Record the render duration for the fiber that errored.1746        stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1747        // Include the time spent working on failed children before continuing.1748        let actualDuration = completedWork.actualDuration;1749        let child = completedWork.child;1750        while (child !== null) {1751          actualDuration += child.actualDuration;1752          child = child.sibling;1753        }1754        completedWork.actualDuration = actualDuration;1755      }1756      if (returnFiber !== null) {1757        // Mark the parent fiber as incomplete and clear its subtree flags.1758        returnFiber.flags |= Incomplete;1759        returnFiber.subtreeFlags = NoFlags;1760        returnFiber.deletions = null;1761      } else {1762        // We've unwound all the way to the root.1763        workInProgressRootExitStatus = RootDidNotComplete;1764        workInProgress = null;1765        return;1766      }1767    }1768    const siblingFiber = completedWork.sibling;1769    if (siblingFiber !== null) {1770      // If there is more work to do in this returnFiber, do that next.1771      workInProgress = siblingFiber;1772      return;1773    }1774    // Otherwise, return to the parent1775    completedWork = returnFiber;1776    // Update the next thing we're working on in case something throws.1777    workInProgress = completedWork;1778  } while (completedWork !== null);1779  // We've reached the root.1780  if (workInProgressRootExitStatus === RootInProgress) {1781    workInProgressRootExitStatus = RootCompleted;1782  }1783}1784function commitRoot(root: FiberRoot, recoverableErrors: null | Array<mixed>) {1785  // TODO: This no longer makes any sense. We already wrap the mutation and1786  // layout phases. Should be able to remove.1787  const previousUpdateLanePriority = getCurrentUpdatePriority();1788  const prevTransition = ReactCurrentBatchConfig.transition;1789  try {1790    ReactCurrentBatchConfig.transition = null;1791    setCurrentUpdatePriority(DiscreteEventPriority);1792    commitRootImpl(root, recoverableErrors, previousUpdateLanePriority);1793  } finally {1794    ReactCurrentBatchConfig.transition = prevTransition;1795    setCurrentUpdatePriority(previousUpdateLanePriority);1796  }1797  return null;1798}1799function commitRootImpl(1800  root: FiberRoot,1801  recoverableErrors: null | Array<mixed>,1802  renderPriorityLevel: EventPriority,1803) {1804  do {1805    // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which1806    // means `flushPassiveEffects` will sometimes result in additional1807    // passive effects. So we need to keep flushing in a loop until there are1808    // no more pending effects.1809    // TODO: Might be better if `flushPassiveEffects` did not automatically1810    // flush synchronous work at the end, to avoid factoring hazards like this.1811    flushPassiveEffects();1812  } while (rootWithPendingPassiveEffects !== null);1813  flushRenderPhaseStrictModeWarningsInDEV();1814  if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {1815    throw new Error('Should not already be working.');1816  }1817  const finishedWork = root.finishedWork;1818  const lanes = root.finishedLanes;1819  if (__DEV__) {1820    if (enableDebugTracing) {1821      logCommitStarted(lanes);1822    }1823  }1824  if (enableSchedulingProfiler) {1825    markCommitStarted(lanes);1826  }1827  if (finishedWork === null) {1828    if (__DEV__) {1829      if (enableDebugTracing) {1830        logCommitStopped();1831      }1832    }1833    if (enableSchedulingProfiler) {1834      markCommitStopped();1835    }1836    return null;1837  } else {1838    if (__DEV__) {1839      if (lanes === NoLanes) {1840        console.error(1841          'root.finishedLanes should not be empty during a commit. This is a ' +1842            'bug in React.',1843        );1844      }1845    }1846  }1847  root.finishedWork = null;1848  root.finishedLanes = NoLanes;1849  if (finishedWork === root.current) {1850    throw new Error(1851      'Cannot commit the same tree as before. This error is likely caused by ' +1852        'a bug in React. Please file an issue.',1853    );1854  }1855  // commitRoot never returns a continuation; it always finishes synchronously.1856  // So we can clear these now to allow a new callback to be scheduled.1857  root.callbackNode = null;1858  root.callbackPriority = NoLane;1859  // Update the first and last pending times on this root. The new first1860  // pending time is whatever is left on the root fiber.1861  let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);1862  markRootFinished(root, remainingLanes);1863  if (root === workInProgressRoot) {1864    // We can reset these now that they are finished.1865    workInProgressRoot = null;1866    workInProgress = null;1867    workInProgressRootRenderLanes = NoLanes;1868  } else {1869    // This indicates that the last root we worked on is not the same one that1870    // we're committing now. This most commonly happens when a suspended root1871    // times out.1872  }1873  // If there are pending passive effects, schedule a callback to process them.1874  // Do this as early as possible, so it is queued before anything else that1875  // might get scheduled in the commit phase. (See #16714.)1876  // TODO: Delete all other places that schedule the passive effect callback1877  // They're redundant.1878  if (1879    (finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||1880    (finishedWork.flags & PassiveMask) !== NoFlags1881  ) {1882    if (!rootDoesHavePassiveEffects) {1883      rootDoesHavePassiveEffects = true;1884      pendingPassiveEffectsRemainingLanes = remainingLanes;1885      scheduleCallback(NormalSchedulerPriority, () => {1886        flushPassiveEffects();1887        // This render triggered passive effects: release the root cache pool1888        // *after* passive effects fire to avoid freeing a cache pool that may1889        // be referenced by a node in the tree (HostRoot, Cache boundary etc)1890        return null;1891      });1892    }1893  }1894  // Check if there are any effects in the whole tree.1895  // TODO: This is left over from the effect list implementation, where we had1896  // to check for the existence of `firstEffect` to satisfy Flow. I think the1897  // only other reason this optimization exists is because it affects profiling.1898  // Reconsider whether this is necessary.1899  const subtreeHasEffects =1900    (finishedWork.subtreeFlags &1901      (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1902    NoFlags;1903  const rootHasEffect =1904    (finishedWork.flags &1905      (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1906    NoFlags;1907  if (subtreeHasEffects || rootHasEffect) {1908    const prevTransition = ReactCurrentBatchConfig.transition;1909    ReactCurrentBatchConfig.transition = null;1910    const previousPriority = getCurrentUpdatePriority();1911    setCurrentUpdatePriority(DiscreteEventPriority);1912    const prevExecutionContext = executionContext;1913    executionContext |= CommitContext;1914    // Reset this to null before calling lifecycles1915    ReactCurrentOwner.current = null;1916    // The commit phase is broken into several sub-phases. We do a separate pass1917    // of the effect list for each phase: all mutation effects come before all1918    // layout effects, and so on.1919    // The first phase a "before mutation" phase. We use this phase to read the1920    // state of the host tree right before we mutate it. This is where1921    // getSnapshotBeforeUpdate is called.1922    const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(1923      root,1924      finishedWork,1925    );1926    if (enableProfilerTimer) {1927      // Mark the current commit time to be shared by all Profilers in this1928      // batch. This enables them to be grouped later.1929      recordCommitTime();1930    }1931    if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {1932      // Track the root here, rather than in commitLayoutEffects(), because of ref setters.1933      // Updates scheduled during ref detachment should also be flagged.1934      rootCommittingMutationOrLayoutEffects = root;1935    }1936    // The next phase is the mutation phase, where we mutate the host tree.1937    commitMutationEffects(root, finishedWork, lanes);1938    if (enableCreateEventHandleAPI) {1939      if (shouldFireAfterActiveInstanceBlur) {1940        afterActiveInstanceBlur();1941      }1942    }1943    resetAfterCommit(root.containerInfo);1944    // The work-in-progress tree is now the current tree. This must come after1945    // the mutation phase, so that the previous tree is still current during1946    // componentWillUnmount, but before the layout phase, so that the finished1947    // work is current during componentDidMount/Update.1948    root.current = finishedWork;1949    // The next phase is the layout phase, where we call effects that read1950    // the host tree after it's been mutated. The idiomatic use case for this is1951    // layout, but class component lifecycles also fire here for legacy reasons.1952    if (__DEV__) {1953      if (enableDebugTracing) {1954        logLayoutEffectsStarted(lanes);1955      }1956    }1957    if (enableSchedulingProfiler) {1958      markLayoutEffectsStarted(lanes);1959    }1960    commitLayoutEffects(finishedWork, root, lanes);1961    if (__DEV__) {1962      if (enableDebugTracing) {1963        logLayoutEffectsStopped();1964      }1965    }1966    if (enableSchedulingProfiler) {1967      markLayoutEffectsStopped();1968    }1969    if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {1970      rootCommittingMutationOrLayoutEffects = null;1971    }1972    // Tell Scheduler to yield at the end of the frame, so the browser has an1973    // opportunity to paint.1974    requestPaint();1975    executionContext = prevExecutionContext;1976    // Reset the priority to the previous non-sync value.1977    setCurrentUpdatePriority(previousPriority);1978    ReactCurrentBatchConfig.transition = prevTransition;1979  } else {1980    // No effects.1981    root.current = finishedWork;1982    // Measure these anyway so the flamegraph explicitly shows that there were1983    // no effects.1984    // TODO: Maybe there's a better way to report this.1985    if (enableProfilerTimer) {1986      recordCommitTime();1987    }1988  }1989  const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;1990  if (rootDoesHavePassiveEffects) {1991    // This commit has passive effects. Stash a reference to them. But don't1992    // schedule a callback until after flushing layout work.1993    rootDoesHavePassiveEffects = false;1994    rootWithPendingPassiveEffects = root;1995    pendingPassiveEffectsLanes = lanes;1996  } else {1997    // There were no passive effects, so we can immediately release the cache1998    // pool for this render.1999    releaseRootPooledCache(root, remainingLanes);2000  }2001  // Read this again, since an effect might have updated it2002  remainingLanes = root.pendingLanes;2003  // Check if there's remaining work on this root2004  // TODO: This is part of the `componentDidCatch` implementation. Its purpose2005  // is to detect whether something might have called setState inside2006  // `componentDidCatch`. The mechanism is known to be flawed because `setState`2007  // inside `componentDidCatch` is itself flawed â that's why we recommend2008  // `getDerivedStateFromError` instead. However, it could be improved by2009  // checking if remainingLanes includes Sync work, instead of whether there's2010  // any work remaining at all (which would also include stuff like Suspense2011  // retries or transitions). It's been like this for a while, though, so fixing2012  // it probably isn't that urgent.2013  if (remainingLanes === NoLanes) {2014    // If there's no remaining work, we can clear the set of already failed2015    // error boundaries.2016    legacyErrorBoundariesThatAlreadyFailed = null;2017  }2018  if (__DEV__ && enableStrictEffects) {2019    if (!rootDidHavePassiveEffects) {2020      commitDoubleInvokeEffectsInDEV(root.current, false);2021    }2022  }2023  onCommitRootDevTools(finishedWork.stateNode, renderPriorityLevel);2024  if (enableUpdaterTracking) {2025    if (isDevToolsPresent) {2026      root.memoizedUpdaters.clear();2027    }2028  }2029  if (__DEV__) {2030    onCommitRootTestSelector();2031  }2032  // Always call this before exiting `commitRoot`, to ensure that any2033  // additional work on this root is scheduled.2034  ensureRootIsScheduled(root, now());2035  if (recoverableErrors !== null) {2036    // There were errors during this render, but recovered from them without2037    // needing to surface it to the UI. We log them here.2038    const onRecoverableError = root.onRecoverableError;2039    for (let i = 0; i < recoverableErrors.length; i++) {2040      const recoverableError = recoverableErrors[i];2041      onRecoverableError(recoverableError);2042    }2043  }2044  if (hasUncaughtError) {2045    hasUncaughtError = false;2046    const error = firstUncaughtError;2047    firstUncaughtError = null;2048    throw error;2049  }2050  // If the passive effects are the result of a discrete render, flush them2051  // synchronously at the end of the current task so that the result is2052  // immediately observable. Otherwise, we assume that they are not2053  // order-dependent and do not need to be observed by external systems, so we2054  // can wait until after paint.2055  // TODO: We can optimize this by not scheduling the callback earlier. Since we2056  // currently schedule the callback in multiple places, will wait until those2057  // are consolidated.2058  if (2059    includesSomeLane(pendingPassiveEffectsLanes, SyncLane) &&2060    root.tag !== LegacyRoot2061  ) {2062    flushPassiveEffects();2063  }2064  // Read this again, since a passive effect might have updated it2065  remainingLanes = root.pendingLanes;2066  if (includesSomeLane(remainingLanes, (SyncLane: Lane))) {2067    if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {2068      markNestedUpdateScheduled();2069    }2070    // Count the number of times the root synchronously re-renders without2071    // finishing. If there are too many, it indicates an infinite update loop.2072    if (root === rootWithNestedUpdates) {2073      nestedUpdateCount++;2074    } else {2075      nestedUpdateCount = 0;2076      rootWithNestedUpdates = root;2077    }2078  } else {2079    nestedUpdateCount = 0;2080  }2081  // If layout work was scheduled, flush it now.2082  flushSyncCallbacks();2083  if (enableTransitionTracing) {2084    const prevPendingTransitionCallbacks = currentPendingTransitionCallbacks;2085    const prevRootTransitionCallbacks = root.transitionCallbacks;2086    if (2087      prevPendingTransitionCallbacks !== null &&2088      prevRootTransitionCallbacks !== null2089    ) {2090      // TODO(luna) Refactor this code into the Host Config2091      const endTime = now();2092      currentPendingTransitionCallbacks = null;2093      scheduleCallback(IdleSchedulerPriority, () =>2094        processTransitionCallbacks(2095          prevPendingTransitionCallbacks,2096          endTime,2097          prevRootTransitionCallbacks,2098        ),2099      );2100    }2101  }2102  if (__DEV__) {2103    if (enableDebugTracing) {2104      logCommitStopped();2105    }2106  }2107  if (enableSchedulingProfiler) {2108    markCommitStopped();2109  }2110  return null;2111}2112function releaseRootPooledCache(root: FiberRoot, remainingLanes: Lanes) {2113  if (enableCache) {2114    const pooledCacheLanes = (root.pooledCacheLanes &= remainingLanes);2115    if (pooledCacheLanes === NoLanes) {2116      // None of the remaining work relies on the cache pool. Clear it so2117      // subsequent requests get a new cache2118      const pooledCache = root.pooledCache;2119      if (pooledCache != null) {2120        root.pooledCache = null;2121        releaseCache(pooledCache);2122      }2123    }2124  }2125}2126export function flushPassiveEffects(): boolean {2127  // Returns whether passive effects were flushed.2128  // TODO: Combine this check with the one in flushPassiveEFfectsImpl. We should2129  // probably just combine the two functions. I believe they were only separate2130  // in the first place because we used to wrap it with2131  // `Scheduler.runWithPriority`, which accepts a function. But now we track the2132  // priority within React itself, so we can mutate the variable directly.2133  if (rootWithPendingPassiveEffects !== null) {2134    // Cache the root since rootWithPendingPassiveEffects is cleared in2135    // flushPassiveEffectsImpl2136    const root = rootWithPendingPassiveEffects;2137    // Cache and clear the remaining lanes flag; it must be reset since this2138    // method can be called from various places, not always from commitRoot2139    // where the remaining lanes are known2140    const remainingLanes = pendingPassiveEffectsRemainingLanes;2141    pendingPassiveEffectsRemainingLanes = NoLanes;2142    const renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes);2143    const priority = lowerEventPriority(DefaultEventPriority, renderPriority);2144    const prevTransition = ReactCurrentBatchConfig.transition;2145    const previousPriority = getCurrentUpdatePriority();2146    try {2147      ReactCurrentBatchConfig.transition = null;2148      setCurrentUpdatePriority(priority);2149      return flushPassiveEffectsImpl();2150    } finally {2151      setCurrentUpdatePriority(previousPriority);2152      ReactCurrentBatchConfig.transition = prevTransition;2153      // Once passive effects have run for the tree - giving components a2154      // chance to retain cache instances they use - release the pooled2155      // cache at the root (if there is one)2156      releaseRootPooledCache(root, remainingLanes);2157    }2158  }2159  return false;2160}2161export function enqueuePendingPassiveProfilerEffect(fiber: Fiber): void {2162  if (enableProfilerTimer && enableProfilerCommitHooks) {2163    pendingPassiveProfilerEffects.push(fiber);2164    if (!rootDoesHavePassiveEffects) {2165      rootDoesHavePassiveEffects = true;2166      scheduleCallback(NormalSchedulerPriority, () => {2167        flushPassiveEffects();2168        return null;2169      });2170    }2171  }2172}2173function flushPassiveEffectsImpl() {2174  if (rootWithPendingPassiveEffects === null) {2175    return false;2176  }2177  const root = rootWithPendingPassiveEffects;2178  const lanes = pendingPassiveEffectsLanes;2179  rootWithPendingPassiveEffects = null;2180  // TODO: This is sometimes out of sync with rootWithPendingPassiveEffects.2181  // Figure out why and fix it. It's not causing any known issues (probably2182  // because it's only used for profiling), but it's a refactor hazard.2183  pendingPassiveEffectsLanes = NoLanes;2184  if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {2185    throw new Error('Cannot flush passive effects while already rendering.');2186  }2187  if (__DEV__) {2188    if (enableDebugTracing) {2189      logPassiveEffectsStarted(lanes);2190    }2191  }2192  if (enableSchedulingProfiler) {2193    markPassiveEffectsStarted(lanes);2194  }2195  const prevExecutionContext = executionContext;2196  executionContext |= CommitContext;2197  commitPassiveUnmountEffects(root.current);2198  commitPassiveMountEffects(root, root.current);2199  // TODO: Move to commitPassiveMountEffects2200  if (enableProfilerTimer && enableProfilerCommitHooks) {2201    const profilerEffects = pendingPassiveProfilerEffects;2202    pendingPassiveProfilerEffects = [];2203    for (let i = 0; i < profilerEffects.length; i++) {2204      const fiber = ((profilerEffects[i]: any): Fiber);2205      commitPassiveEffectDurations(root, fiber);2206    }2207  }2208  if (__DEV__) {2209    if (enableDebugTracing) {2210      logPassiveEffectsStopped();2211    }2212  }2213  if (enableSchedulingProfiler) {2214    markPassiveEffectsStopped();2215  }2216  if (__DEV__ && enableStrictEffects) {2217    commitDoubleInvokeEffectsInDEV(root.current, true);2218  }2219  executionContext = prevExecutionContext;2220  flushSyncCallbacks();2221  // If additional passive effects were scheduled, increment a counter. If this2222  // exceeds the limit, we'll fire a warning.2223  nestedPassiveUpdateCount =2224    rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;2225  // TODO: Move to commitPassiveMountEffects2226  onPostCommitRootDevTools(root);2227  if (enableProfilerTimer && enableProfilerCommitHooks) {2228    const stateNode = root.current.stateNode;2229    stateNode.effectDuration = 0;2230    stateNode.passiveEffectDuration = 0;2231  }2232  return true;2233}2234export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {2235  return (2236    legacyErrorBoundariesThatAlreadyFailed !== null &&2237    legacyErrorBoundariesThatAlreadyFailed.has(instance)2238  );2239}2240export function markLegacyErrorBoundaryAsFailed(instance: mixed) {2241  if (legacyErrorBoundariesThatAlreadyFailed === null) {2242    legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);2243  } else {2244    legacyErrorBoundariesThatAlreadyFailed.add(instance);2245  }2246}2247function prepareToThrowUncaughtError(error: mixed) {2248  if (!hasUncaughtError) {2249    hasUncaughtError = true;2250    firstUncaughtError = error;2251  }2252}2253export const onUncaughtError = prepareToThrowUncaughtError;2254function captureCommitPhaseErrorOnRoot(2255  rootFiber: Fiber,2256  sourceFiber: Fiber,2257  error: mixed,2258) {2259  const errorInfo = createCapturedValue(error, sourceFiber);2260  const update = createRootErrorUpdate(rootFiber, errorInfo, (SyncLane: Lane));2261  enqueueUpdate(rootFiber, update, (SyncLane: Lane));2262  const eventTime = requestEventTime();2263  const root = markUpdateLaneFromFiberToRoot(rootFiber, (SyncLane: Lane));2264  if (root !== null) {2265    markRootUpdated(root, SyncLane, eventTime);2266    ensureRootIsScheduled(root, eventTime);2267  }2268}2269export function captureCommitPhaseError(2270  sourceFiber: Fiber,2271  nearestMountedAncestor: Fiber | null,2272  error: mixed,2273) {2274  if (sourceFiber.tag === HostRoot) {2275    // Error was thrown at the root. There is no parent, so the root2276    // itself should capture it.2277    captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);2278    return;2279  }2280  let fiber = null;2281  if (skipUnmountedBoundaries) {2282    fiber = nearestMountedAncestor;2283  } else {2284    fiber = sourceFiber.return;2285  }2286  while (fiber !== null) {2287    if (fiber.tag === HostRoot) {2288      captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);2289      return;2290    } else if (fiber.tag === ClassComponent) {2291      const ctor = fiber.type;2292      const instance = fiber.stateNode;2293      if (2294        typeof ctor.getDerivedStateFromError === 'function' ||2295        (typeof instance.componentDidCatch === 'function' &&2296          !isAlreadyFailedLegacyErrorBoundary(instance))2297      ) {2298        const errorInfo = createCapturedValue(error, sourceFiber);2299        const update = createClassErrorUpdate(2300          fiber,2301          errorInfo,2302          (SyncLane: Lane),2303        );2304        enqueueUpdate(fiber, update, (SyncLane: Lane));2305        const eventTime = requestEventTime();2306        const root = markUpdateLaneFromFiberToRoot(fiber, (SyncLane: Lane));2307        if (root !== null) {2308          markRootUpdated(root, SyncLane, eventTime);2309          ensureRootIsScheduled(root, eventTime);2310        }2311        return;2312      }2313    }2314    fiber = fiber.return;2315  }2316  if (__DEV__) {2317    // TODO: Until we re-land skipUnmountedBoundaries (see #20147), this warning2318    // will fire for errors that are thrown by destroy functions inside deleted2319    // trees. What it should instead do is propagate the error to the parent of2320    // the deleted tree. In the meantime, do not add this warning to the2321    // allowlist; this is only for our internal use.2322    console.error(2323      'Internal React error: Attempted to capture a commit phase error ' +2324        'inside a detached tree. This indicates a bug in React. Likely ' +2325        'causes include deleting the same fiber more than once, committing an ' +2326        'already-finished tree, or an inconsistent return pointer.\n\n' +2327        'Error message:\n\n%s',2328      error,2329    );2330  }2331}2332export function pingSuspendedRoot(2333  root: FiberRoot,2334  wakeable: Wakeable,2335  pingedLanes: Lanes,2336) {2337  const pingCache = root.pingCache;2338  if (pingCache !== null) {2339    // The wakeable resolved, so we no longer need to memoize, because it will2340    // never be thrown again.2341    pingCache.delete(wakeable);2342  }2343  const eventTime = requestEventTime();2344  markRootPinged(root, pingedLanes, eventTime);2345  warnIfSuspenseResolutionNotWrappedWithActDEV(root);2346  if (2347    workInProgressRoot === root &&2348    isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)2349  ) {2350    // Received a ping at the same priority level at which we're currently2351    // rendering. We might want to restart this render. This should mirror2352    // the logic of whether or not a root suspends once it completes.2353    // TODO: If we're rendering sync either due to Sync, Batched or expired,2354    // we should probably never restart.2355    // If we're suspended with delay, or if it's a retry, we'll always suspend2356    // so we can always restart.2357    if (2358      workInProgressRootExitStatus === RootSuspendedWithDelay ||2359      (workInProgressRootExitStatus === RootSuspended &&2360        includesOnlyRetries(workInProgressRootRenderLanes) &&2361        now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)2362    ) {2363      // Restart from the root.2364      prepareFreshStack(root, NoLanes);2365    } else {2366      // Even though we can't restart right now, we might get an2367      // opportunity later. So we mark this render as having a ping.2368      workInProgressRootPingedLanes = mergeLanes(2369        workInProgressRootPingedLanes,2370        pingedLanes,2371      );2372    }2373  }2374  ensureRootIsScheduled(root, eventTime);2375}2376function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {2377  // The boundary fiber (a Suspense component or SuspenseList component)2378  // previously was rendered in its fallback state. One of the promises that2379  // suspended it has resolved, which means at least part of the tree was2380  // likely unblocked. Try rendering again, at a new lanes.2381  if (retryLane === NoLane) {2382    // TODO: Assign this to `suspenseState.retryLane`? to avoid2383    // unnecessary entanglement?2384    retryLane = requestRetryLane(boundaryFiber);2385  }2386  // TODO: Special case idle priority?2387  const eventTime = requestEventTime();2388  const root = markUpdateLaneFromFiberToRoot(boundaryFiber, retryLane);2389  if (root !== null) {2390    markRootUpdated(root, retryLane, eventTime);2391    ensureRootIsScheduled(root, eventTime);2392  }2393}2394export function retryDehydratedSuspenseBoundary(boundaryFiber: Fiber) {2395  const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2396  let retryLane = NoLane;2397  if (suspenseState !== null) {2398    retryLane = suspenseState.retryLane;2399  }2400  retryTimedOutBoundary(boundaryFiber, retryLane);2401}2402export function resolveRetryWakeable(boundaryFiber: Fiber, wakeable: Wakeable) {2403  let retryLane = NoLane; // Default2404  let retryCache: WeakSet<Wakeable> | Set<Wakeable> | null;...ReactFiberLane.js
Source:ReactFiberLane.js  
1import {2  ImmediatePriority as ImmediateSchedulerPriority,3  UserBlockingPriority as UserBlockingSchedulerPriority,4  NormalPriority as NormalSchedulerPriority,5  LowPriority as LowSchedulerPriority,6  IdlePriority as IdleSchedulerPriority,7  NoPriority as NoSchedulerPriority,8} from './SchedulerWithReactIntegration';9const SyncLanePriority = 15;10const SyncBatchedLanePriority = 14;11const InputDiscreteHydrationLanePriority = 13;12const InputDiscreteLanePriority = 12;13const InputContinuousHydrationLanePriority = 11;14const InputContinuousLanePriority = 10;15const DefaultHydrationLanePriority = 9;16const DefaultLanePriority = 8;17const TransitionHydrationPriority = 7;18const TransitionPriority = 6;19const RetryLanePriority = 5;20const SelectiveHydrationLanePriority = 4;21const IdleHydrationLanePriority = 3;22const IdleLanePriority = 2;23const OffscreenLanePriority = 1;24const NoLanePriority = 0;25const createLaneMap = (initial) =>26  Array(31)27    .fill(0)28    .map(() => initial);29const NoLanes = 0b0000000000000000000000000000000;30const NoLane = 0b0000000000000000000000000000000;31const SyncLane = 0b0000000000000000000000000000001;32const SyncBatchedLane = 0b0000000000000000000000000000010;33const InputDiscreteHydrationLane = 0b0000000000000000000000000000100;34const InputDiscreteLanes = 0b0000000000000000000000000011000;35const InputContinuousHydrationLane = 0b0000000000000000000000000100000;36const InputContinuousLanes = 0b0000000000000000000000011000000;37const DefaultHydrationLane = 0b0000000000000000000000100000000;38const DefaultLanes = 0b0000000000000000000111000000000;39const TransitionHydrationLane = 0b0000000000000000001000000000000;40const TransitionLanes = 0b0000000001111111110000000000000;41const IdleHydrationLane = 0b0001000000000000000000000000000;42const IdleLanes = 0b0110000000000000000000000000000;43const NonIdleLanes = 0b0000111111111111111111111111111;44const OffscreenLane = 0b1000000000000000000000000000000;45const NoTimestamp = -1;46const getHighestPriorityLane = (lanes) => lanes & -lanes;47const pickArbitraryLane = (lanes) => getHighestPriorityLane(lanes);48const findUpdateLane = (lanePriority, wipLanes) => {49  let lane;50  switch (lanePriority) {51    case NoLanePriority:52      break;53    case SyncLanePriority:54      return SyncLane;55    case SyncBatchedLanePriority:56      return SyncBatchedLane;57    case InputDiscreteLanePriority: {58      lane = pickArbitraryLane(InputDiscreteLanes & ~wipLanes);59      if (lane === NoLane) {60        return findUpdateLane(InputContinuousLanePriority, wipLanes);61      }62      return lane;63    }64    case InputContinuousLanePriority: {65      lane = pickArbitraryLane(InputContinuousLanes & ~wipLanes);66      if (lane === NoLane) {67        return findUpdateLane(DefaultLanePriority, wipLanes);68      }69      return lane;70    }71    case DefaultLanePriority: {72      lane = pickArbitraryLane(DefaultLanes & ~wipLanes);73      if (lane === NoLane) {74        lane = pickArbitraryLane(TransitionLanes & ~wipLanes);75        if (lane === NoLane) {76          lane = pickArbitraryLane(DefaultLanes);77        }78      }79      return lane;80    }81    case TransitionPriority:82    case RetryLanePriority:83      break;84    case IdleLanePriority:85      lane = pickArbitraryLane(IdleLanes & ~wipLanes);86      if (lane === NoLane) {87        lane = pickArbitraryLane(IdleLanes);88      }89      return lane;90    default:91      break;92  }93  throw new Error('Invalid update priority: %s. This is a bug in React.');94};95const schedulerPriorityToLanePriority = (schedulerPriorityLevel) => {96  switch (schedulerPriorityLevel) {97    case ImmediateSchedulerPriority:98      return SyncLanePriority;99    case UserBlockingSchedulerPriority:100      return InputContinuousLanePriority;101    case NormalSchedulerPriority:102    case LowSchedulerPriority:103      return DefaultLanePriority;104    case IdleSchedulerPriority:105      return IdleLanePriority;106    default:107      return NoLanePriority;108  }109};110const isSubsetOfLanes = (set, subset) => (set & subset) === subset;111const mergeLanes = (a, b) => a | b;112const pickArbitraryLaneIndex = (lane) => 31 - Math.clz32(lane);113const markRootUpdated = (root, updateLane, eventTime) => {114  root.pendingLanes |= updateLane;115  const higherPriorityLanes = updateLane - 1;116  root.suspendedLanes &= higherPriorityLanes;117  root.pingedLanes &= higherPriorityLanes;118  const eventTimes = root.eventTimes;119  const index = pickArbitraryLaneIndex(updateLane);120  eventTimes[index] = eventTime;121};122const markRootSuspended = (root, suspendedLanes) => {123  root.suspendedLanes |= suspendedLanes;124  root.pingedLanes &= ~suspendedLanes;125  const expirationTimes = root.expirationTimes;126  let lanes = suspendedLanes;127  while (lanes > 0) {128    const index = pickArbitraryLaneIndex(lanes);129    const lane = 1 << index;130    expirationTimes[index] = NoTimestamp;131    lanes &= ~lane;132  }133};134const includesSomeLane = (a, b) => (a & b) !== NoLanes;135let return_highestLanePriority = DefaultLanePriority;136const getHighestPriorityLanes = (lanes) => {137  if ((SyncLane & lanes) !== NoLanes) {138    return_highestLanePriority = SyncLanePriority;139    return SyncLane;140  }141  if ((SyncBatchedLane & lanes) !== NoLanes) {142    return_highestLanePriority = SyncBatchedLanePriority;143    return SyncBatchedLane;144  }145  if ((InputDiscreteHydrationLane & lanes) !== NoLanes) {146    return_highestLanePriority = InputDiscreteHydrationLanePriority;147    return InputDiscreteHydrationLane;148  }149  const inputDiscreteLanes = InputDiscreteLanes & lanes;150  if (inputDiscreteLanes !== NoLanes) {151    return_highestLanePriority = InputDiscreteLanePriority;152    return inputDiscreteLanes;153  }154  if ((lanes & InputContinuousHydrationLane) !== NoLanes) {155    return_highestLanePriority = InputContinuousHydrationLanePriority;156    return InputContinuousHydrationLane;157  }158  const inputContinuousLanes = InputContinuousLanes & lanes;159  if (inputContinuousLanes !== NoLanes) {160    return_highestLanePriority = InputContinuousLanePriority;161    return inputContinuousLanes;162  }163  if ((lanes & DefaultHydrationLane) !== NoLanes) {164    return_highestLanePriority = DefaultHydrationLanePriority;165    return DefaultHydrationLane;166  }167  const defaultLanes = DefaultLanes & lanes;168  if (defaultLanes !== NoLanes) {169    return_highestLanePriority = DefaultLanePriority;170    return defaultLanes;171  }172  if ((lanes & TransitionHydrationLane) !== NoLanes) {173    return_highestLanePriority = TransitionHydrationPriority;174    return TransitionHydrationLane;175  }176  const transitionLanes = TransitionLanes & lanes;177  if (transitionLanes !== NoLanes) {178    return_highestLanePriority = TransitionPriority;179    return transitionLanes;180  }181  const retryLanes = RetryLanes & lanes;182  if (retryLanes !== NoLanes) {183    return_highestLanePriority = RetryLanePriority;184    return retryLanes;185  }186  if (lanes & SelectiveHydrationLane) {187    return_highestLanePriority = SelectiveHydrationLanePriority;188    return SelectiveHydrationLane;189  }190  if ((lanes & IdleHydrationLane) !== NoLanes) {191    return_highestLanePriority = IdleHydrationLanePriority;192    return IdleHydrationLane;193  }194  const idleLanes = IdleLanes & lanes;195  if (idleLanes !== NoLanes) {196    return_highestLanePriority = IdleLanePriority;197    return idleLanes;198  }199  if ((OffscreenLane & lanes) !== NoLanes) {200    return_highestLanePriority = OffscreenLanePriority;201    return OffscreenLane;202  }203  return_highestLanePriority = DefaultLanePriority;204  return lanes;205};206const getLowestPriorityLane = (lanes) => {207  const index = 31 - Math.clz32(lanes);208  return index < 0 ? NoLanes : 1 << index;209};210const getNextLanes = (root, wipLanes) => {211  const pendingLanes = root.pendingLanes;212  if (pendingLanes === NoLanes) {213    return_highestLanePriority = NoLanePriority;214    return NoLanes;215  }216  let nextLanes = NoLanes;217  let nextLanePriority = NoLanePriority;218  const expiredLanes = root.expiredLanes;219  const suspendedLanes = root.suspendedLanes;220  const pingedLanes = root.pingedLanes;221  if (expiredLanes !== NoLanes) {222    nextLanes = expiredLanes;223    nextLanePriority = return_highestLanePriority = SyncLanePriority;224  } else {225    const nonIdlePendingLanes = pendingLanes & NonIdleLanes;226    if (nonIdlePendingLanes !== NoLanes) {227      const nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes;228      if (nonIdleUnblockedLanes !== NoLanes) {229        nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes);230        nextLanePriority = return_highestLanePriority;231      } else {232        const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes;233        if (nonIdlePingedLanes !== NoLanes) {234          nextLanes = getHighestPriorityLanes(nonIdlePingedLanes);235          nextLanePriority = return_highestLanePriority;236        }237      }238    } else {239      const unblockedLanes = pendingLanes & ~suspendedLanes;240      if (unblockedLanes !== NoLanes) {241        nextLanes = getHighestPriorityLanes(unblockedLanes);242        nextLanePriority = return_highestLanePriority;243      } else {244        if (pingedLanes !== NoLanes) {245          nextLanes = getHighestPriorityLanes(pingedLanes);246          nextLanePriority = return_highestLanePriority;247        }248      }249    }250  }251  if (nextLanes === NoLanes) {252    return NoLanes;253  }254  nextLanes = pendingLanes & ((getLowestPriorityLane(nextLanes) << 1) - 1);255  if (256    wipLanes !== NoLanes &&257    wipLanes !== nextLanes &&258    (wipLanes & suspendedLanes) === NoLanes259  ) {260    getHighestPriorityLanes(wipLanes);261    const wipLanePriority = return_highestLanePriority;262    if (nextLanePriority <= wipLanePriority) {263      return wipLanes;264    } else {265      return_highestLanePriority = nextLanePriority;266    }267  }268  const entangledLanes = root.entangledLanes;269  if (entangledLanes !== NoLanes) {270    const entanglements = root.entanglements;271    let lanes = nextLanes & entangledLanes;272    while (lanes > 0) {273      const index = pickArbitraryLaneIndex(lanes);274      const lane = 1 << index;275      nextLanes |= entanglements[index];276      lanes &= ~lane;277    }278  }279  return nextLanes;280};281const markRootFinished = (root, remainingLanes) => {282  const noLongerPendingLanes = root.pendingLanes & ~remainingLanes;283  root.pendingLanes = remainingLanes;284  root.suspendedLanes = 0;285  root.pingedLanes = 0;286  root.expiredLanes &= remainingLanes;287  root.mutableReadLanes &= remainingLanes;288  root.entangledLanes &= remainingLanes;289  const entanglements = root.entanglements;290  const eventTimes = root.eventTimes;291  const expirationTimes = root.expirationTimes;292  let lanes = noLongerPendingLanes;293  while (lanes > 0) {294    const index = pickArbitraryLaneIndex(lanes);295    const lane = 1 << index;296    entanglements[index] = NoLanes;297    eventTimes[index] = NoTimestamp;298    expirationTimes[index] = NoTimestamp;299    lanes &= ~lane;300  }301};302const hasDiscreteLanes = (lanes) => (lanes & InputDiscreteLanes) !== NoLanes;303const computeExpirationTime = (lane, currentTime) => {304  getHighestPriorityLanes(lane);305  const priority = return_highestLanePriority;306  if (priority >= InputContinuousLanePriority) {307    return currentTime + 250;308  } else if (priority >= TransitionPriority) {309    return currentTime + 5000;310  } else {311    return NoTimestamp;312  }313};314const markStarvedLanesAsExpired = (root, currentTime) => {315  const pendingLanes = root.pendingLanes;316  const suspendedLanes = root.suspendedLanes;317  const pingedLanes = root.pingedLanes;318  const expirationTimes = root.expirationTimes;319  let lanes = pendingLanes;320  while (lanes > 0) {321    const index = pickArbitraryLaneIndex(lanes);322    const lane = 1 << index;323    const expirationTime = expirationTimes[index];324    if (expirationTime === NoTimestamp) {325      if (326        (lane & suspendedLanes) === NoLanes ||327        (lane & pingedLanes) !== NoLanes328      ) {329        expirationTimes[index] = computeExpirationTime(lane, currentTime);330      }331    } else if (expirationTime <= currentTime) {332      root.expiredLanes |= lane;333    }334    lanes &= ~lane;335  }336};337const returnNextLanesPriority = () => return_highestLanePriority;338const lanePriorityToSchedulerPriority = (lanePriority) => {339  switch (lanePriority) {340    case SyncLanePriority:341    case SyncBatchedLanePriority:342      return ImmediateSchedulerPriority;343    case InputDiscreteHydrationLanePriority:344    case InputDiscreteLanePriority:345    case InputContinuousHydrationLanePriority:346    case InputContinuousLanePriority:347      return UserBlockingSchedulerPriority;348    case DefaultHydrationLanePriority:349    case DefaultLanePriority:350    case TransitionHydrationPriority:351    case TransitionPriority:352    case SelectiveHydrationLanePriority:353    case RetryLanePriority:354      return NormalSchedulerPriority;355    case IdleHydrationLanePriority:356    case IdleLanePriority:357    case OffscreenLanePriority:358      return IdleSchedulerPriority;359    case NoLanePriority:360      return NoSchedulerPriority;361    default:362      invariant(363        false,364        'Invalid update priority: %s. This is a bug in React.',365        lanePriority366      );367  }368};369export {370  SyncLanePriority,371  SyncBatchedLanePriority,372  InputDiscreteLanePriority,373  InputContinuousLanePriority,374  DefaultLanePriority,375  TransitionPriority,376  NoLanePriority,377  createLaneMap,378  NoLanes,379  NoLane,380  SyncLane,381  SyncBatchedLane,382  InputDiscreteHydrationLane,383  DefaultHydrationLane,384  DefaultLanes,385  IdleHydrationLane,386  OffscreenLane,387  NoTimestamp,388  pickArbitraryLane,389  findUpdateLane,390  schedulerPriorityToLanePriority,391  isSubsetOfLanes,392  mergeLanes,393  markRootUpdated,394  markRootSuspended,395  includesSomeLane,396  getNextLanes,397  markRootFinished,398  hasDiscreteLanes,399  markStarvedLanesAsExpired,400  returnNextLanesPriority,401  lanePriorityToSchedulerPriority,...FiberWorkLoop.js
Source:FiberWorkLoop.js  
...82}83export function scheduleUpdateOnFiber(fiber, lane, eventTime){84  const root = markUpdateLaneFromChildToRoot(fiber, lane);85  // update root.pendingLanes, eventTimes etc.86  markRootUpdated(root, lane, eventTime);87  ensureRootIsScheduled(root, eventTime);88  return root;89}90function markUpdateLaneFromChildToRoot(sourceFiber, lane){91  sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);92  let alternate = sourceFiber.alternate;93  if (alternate !== null){94    alternate.lanes = mergeLanes(alternate.lanes, lane);95  }96  let node = sourceFiber;97  let parent = sourceFiber.return;98  while(parent !== null){99    parent.childLanes = mergeLanes(parent.childLanes, lane);100    alternate = parent.alternate;101    if(alternate !== null){102      alternate.childLanes = mergeLanes(alternate.childLanes, lane);103    }104    node = parent;105    parent = parent.return;106  }107  if (node.tag === HostRoot){108    return node.stateNode109  } 110  return null;111}112function ensureRootIsScheduled(root, currentTime){113  // update root.expirationTime. 114  markStarvedLanesAsExpired(root, currentTime);115  const nextLanes = getNextLanes(116    root, 117    root === wipRoot ? wipRootRenderLanes : NoLanes,118  );119  if (nextLanes === NoLanes){120    return;121  }122  const newCallbackPriority = getHighestPriorityLane(nextLanes);123  // Reuse existing task with the same priority.124  const existingCallbackPriority = root.callbackPriority;125  if (existingCallbackPriority === newCallbackPriority){126    return;127  }128  let newCallbackNode = scheduleCallback(129    performConcurrentWorkOnRoot.bind(null, root),130  );131  root.callbackPriority = newCallbackPriority;132  root.callbackNode = newCallbackNode;133}134// Entry point for every concurrent task, i.e. anything that135// goes through Scheduler.136function performConcurrentWorkOnRoot(root){137  currentEventTime = NoTimestamp;138  const originalCallbackNode = root.callbackNode;139  let lanes = getNextLanes(140    root,141    root === wipRoot ? wipRootRenderLanes : NoLanes,142  )143  let exitStatus = renderRootConcurrent(root, lanes); 144  if(exitStatus !== RootIncomplete){145    if(exitStatus === RootErrored){146      executionContext |= RootErrored;147      return null;148    }149    // now we have a consistent tree and ready to commit.150    const finishedWork = root.current.alternate151    root.finishedWork = finishedWork;152    root.finishedLanes = lanes;153    finishConcurrentRender(root, exitStatus, lanes);154  }155  //schedule new tasks found in Render Phase156  ensureRootIsScheduled(root, performance.now());157  // root.callbackNode is always relevant to a task which hasn't completely 158  // finished due to expiration or some other reasons and it will be set to 159  // null in Commit Phase.160  if (root.callbackNode === originalCallbackNode){161    return performConcurrentWorkOnRoot.bind(null, root);162  }163  return null;164}165function finishConcurrentRender(root, exitStatus, lanes){166  switch (exitStatus){167    case RootCompleted:{168      commitRoot(root);169      break;170    }171    case RootSuspendedWithDelay:172    case RootSuspended:{  173      markRootSuspended(root, lanes);174      // work expired. Commit immediately.175      commitRoot(root);176      break;177    }178  }179}180export function pushRenderLanes(fiber, lanes){181  push(subtreeRenderLanesCursor, subtreeRenderLanes);182  subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);183  wipRootIncludedLanes = mergeLanes(184    wipRootIncludedLanes,185    lanes,186  );187}188export function popRenderLanes(){189  subtreeRenderLanes = subtreeRenderLanesCursor.current;190  pop(subtreeRenderLanesCursor);191}192function prepareFreshStack(root, lanes){193  if (wip !== null){194    console.error('Leftover work found:', wip);195  }196  wipRoot = root;197  wip = createWorkInProgress(root.current);198  wipRootRenderLanes = subtreeRenderLanes = wipRootIncludedLanes = lanes;199  wipRootExitStatus = RootIncomplete;200  wipRootSkippedLanes = wipRootUpdatedLanes = wipRootPingedLanes = NoLanes;201}202function handleError(root, thrownValue){203  let erroredWork = wip;204  try {205    throwException(206      root,207      erroredWork.return,208      erroredWork,209      thrownValue,210      wipRootRenderLanes211    );212    completeUnitOfWork(erroredWork);213  } catch (yetAnotherThrownValue){214    console.error(yetAnotherThrownValue);215  }216}217export function markSkippedUpdateLanes(lane){218  wipRootSkippedLanes = mergeLanes(219    lane, 220    wipRootSkippedLanes,221  )222}223export function renderDidSuspend(){224  if(wipRootExitStatus === RootIncomplete){225    wipRootExitStatus = RootSuspended;226  }227}228export function renderDidSuspendDelayIfPossible(){229  if(230    wipRootExitStatus === RootIncomplete ||231    wipRootExitStatus === RootSuspended232  ){233    wipRootExitStatus = RootSuspendedWithDelay;234  }235  if(236    wipRoot !== null &&237    (includesNonIdleWork(wipRootSkippedLanes) ||238      includesNonIdleWork(wipRootUpdatedLanes))239  ){240    markRootSuspended(wipRoot, wipRootRenderLanes);241  }242}243export function renderDidError(){244  if (wipRootExitStatus !== RootCompleted){245    wipRootExitStatus = RootErrored;246  }247}248function renderRootConcurrent(root, lanes){249  const prevExecutionContext = executionContext;250  executionContext |= RenderContext;251  // If the root or lanes have changed, throw out the existing stack252  // and prepare a fresh one. Otherwise we'll continue where we left off.253  if (wipRoot !== root || wipRootRenderLanes !== lanes){254    //create a new FiberNode by cloning root.current and set it to wip.255    prepareFreshStack(root, lanes);256  }257  //Keep trying until all caught errors handled.258  do{259    try {260      workLoopConcurrent();261      break;262    } catch(thrownValue){263      handleError(root, thrownValue);264    }265  } while (true);266  267  executionContext = prevExecutionContext;268  if(wip !== null){269    return RootIncomplete;270  }271  wipRoot = null;272  wipRootRenderLanes = NoLanes273  return wipRootExitStatus;274}275function workLoopConcurrent(){276  // Perform work until Scheduler asks us to yield277  while(wip !== null && !shouldYieldToHost()){278    performUnitOfWork(wip);279  }280}281function performUnitOfWork(unitOfWork){282  const current = unitOfWork.alternate;283  let next = beginWork(current, unitOfWork, subtreeRenderLanes);284  unitOfWork.memoizedProps = unitOfWork.pendingProps;285  if (next === null){286    // If this doesn't spawn new work, complete the current work.287    completeUnitOfWork(unitOfWork);288  } else {289    wip = next;290  }291}292function completeUnitOfWork(unitOfWork){293  // Attempt to complete the current unit of work, then move to the next294  // sibling. If there are no more siblings, return to the parent fiber.295  let completedWork = unitOfWork;296  do {297    const current = completedWork.alternate;298    const returnFiber = completedWork.return;299    if ((completedWork.flags & Incomplete) === NoFlags){    300      let next = completeWork(current, completedWork, subtreeRenderLanes);301      if (next !== null) {302        wip = next;303        return;304      }305    } else {306      // Error threw307      const next = unwindWork(completedWork, subtreeRenderLanes);308      if (next !== null){309        // Error fixed and return to normal render phase.310        next.flags &= HostEffectMask;311        wip = next;312        return;313      }314      if (returnFiber!==null){315        returnFiber.flags |= Incomplete;316        returnFiber.subtreeFlags = NoFlags;317        returnFiber.deletions = null;318      }319    }320    const siblingFiber = completedWork.sibling;321    if (siblingFiber !== null) {322      wip = siblingFiber;323      return;324    }325    completedWork = returnFiber;326    // when reached the root, returnFiber is null, set wip to null to make sure performUnitOfWork() in workLoopConcurrent() wont keep running.327    wip = completedWork;328  } while (completedWork !== null);329  // We've reached the root.330  if (wipRootExitStatus === RootIncomplete) {331    wipRootExitStatus = RootCompleted;332  }333}334function commitRoot(root){335  const finishedWork = root.finishedWork;336  const lanes = root.finishedLanes;337  root.finishedWork = null;338  root.finishedLanes = NoLanes;339  root.callbackNode = null;340  root.callbackPriority = NoLane;341  let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);342  markRootFinished(root, remainingLanes);  343  // schedule a callback to process pending passive effects.344  if (345    (finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||346    (finishedWork.flags & PassiveMask) !== NoFlags347  ){348    if(!rootDoesHavePassiveEffects){349      rootDoesHavePassiveEffects = true;350      scheduleCallback(()=>{351        flushPassiveEffects();352        return null;353      })354    }355  }356  const subtreeHasEffects = 357    (finishedWork.subtreeFlags &358      (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !== 359    NoFlags;360  const rootHasEffect = 361    (finishedWork.flags &362      (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==363    NoFlags;364  if (subtreeHasEffects || rootHasEffect){365    const prevExecutionContext= executionContext;366    executionContext |= CommitContext;367    commitBeforeMutationEffects(finishedWork);368    commitMutationEffects(root, finishedWork);369    commitLayoutEffects(finishedWork, root, lanes);370    371    executionContext = prevExecutionContext;372  } 373  root.current = finishedWork;374  375  const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;376  if (rootDoesHavePassiveEffects){377    rootDoesHavePassiveEffects = false;378    rootWithPendingPassiveEffects = root;379    pendingPassiveEffectsLanes = lanes;380  }381  ensureRootIsScheduled(root, performance.now())382}383function flushPassiveEffects(){384  if (pendingPassiveEffectsLanes !== NoLanes){385    flushPassiveEffectsImpl();386  }387  return false;388}389function flushPassiveEffectsImpl(){390  const root = rootWithPendingPassiveEffects;391  const lanes = pendingPassiveEffectsLanes;392  rootWithPendingPassiveEffects = null;393  pendingPassiveEffectsLanes = NoLanes;394  const prevExecutionContext = executionContext;395  executionContext |= CommitContext;396  commitPassiveUnmountEffects(root.current);397  commitPassiveMountEffects(root, root.current);398  executionContext = prevExecutionContext;399}400export function pingSuspendedRoot(root, wakeable, pingedLanes){401  // The earliest attach to catch the change from Promise. And to resolve 402  // Suspended Lanes before Commit Phase.403  const pingCache = root.pingCache;404  if (pingCache !== null){405    pingCache.delete(wakeable);406  }407  const eventTime = requestEventTime();408  ensureRootIsScheduled(root, eventTime);409}410function retryTimedOutBoundary(boundaryFiber, retryLane=NoLane){411  // The boundary fiber (Suspense) previously was rendered in its fallback 412  // state. One of the promises that suspended is has resolved and try 413  // rendering again at a new expiration time.414  if (retryLane === NoLane) {415    retryLane = claimNextRetryLane();416  }417  const eventTime = requestEventTime();418  const root = markUpdateLaneFromChildToRoot(boundaryFiber, retryLane);419  if (root !== null){420    markRootUpdated(root, retryLane, eventTime);421    ensureRootIsScheduled(root, eventTime);422  }423}424export function resolveRetryWakeable(boundaryFiber, wakeable){425  let retryCache = boundaryFiber.stateNode;426  if(retryCache !== null){427    retryCache.delete(wakeable);428  }429  retryTimedOutBoundary(boundaryFiber);...legacy.js
Source:legacy.js  
...111function scheduleUpdateOnFiber(fiber, lane, eventTime) {112  // æ è®°ä»Fiberå°æ ¹çæ´æ°éé113  const root = markUpdateLaneFromFiberToRoot(fiber, lane);114  // æ è®°æ ¹æä¸ä¸ªæèµ·çæ´æ°115  markRootUpdated(root, lane, eventTime);116  // 忥Lane legacyæ´æ°æ¨¡å¼117  if(lane === SyncLane) {118    // renderé¶æ®µ èµ·ç¹119    performSyncWorkOnRoot(root)120  }else {121    // concurrent Mode 122    ensureRootIsScheduled(root, eventTime);123  }...FiberLane.js
Source:FiberLane.js  
...123}124export function removeLanes(set, subset){125  return set & ~subset;126}127export function markRootUpdated(root, updateLane, eventTime){128  root.pendingLanes |= updateLane;129  130  const eventTimes = root.eventTimes;131  const index = laneToIndex(updateLane);132  eventTimes[index] = eventTime;133}134export function markRootSuspended(root, suspendedLanes){135  root.suspendedLanes |= suspendedLanes;136  root.pingedLanes &= ~suspendedLanes;137  // The suspended lanes are no longer CPU-bound. Clear the expiration times.138  const expirationTimes = root.expirationTimes;139  let lanes = suspendedLanes;140  while (lanes > 0){141    const index = laneToIndex(lanes);...ReactFiberWorkLoop.js
Source:ReactFiberWorkLoop.js  
...39  }40}41export function scheduleUpdateOnFiber(fiber, lane = SyncLane, eventTime) {42  //   const root = markUpdateLaneFromFiberToRoot(fiber, lane);43  //   markRootUpdated(root, lane, eventTime);44  if (lane === SyncLane) {45    if (46      // Check if we're inside unbatchedUpdates47      (executionContext & LegacyUnbatchedContext) !== NoContext &&48      // Check if we're not already rendering49      (executionContext & (RenderContext | CommitContext)) === NoContext50    ) {51      performSyncWorkOnRoot(fiber.stateNode);52    }53  }54}55// ä¸ç»è¿è°åº¦å¨ï¼åæ¥ä»»å¡çèµ·ç¹56// This is the entry point for synchronous tasks that don't go57// through Scheduler...Using AI Code Generation
1const playwright = require('playwright');2(async () => {3  const browser = await playwright.chromium.launch();4  const context = await browser.newContext();5  const page = await context.newPage();6  await page.$eval('#hplogo', el => el.remove());7  await page.evaluate(() => window.playwright.markRootUpdated());8  await page.screenshot({ path: 'example.png' });9  await browser.close();10})();Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3  const browser = await chromium.launch();4  const page = await browser.newPage();5  await page.markRootUpdated();6  await browser.close();7})();8Page.markAsSecure()9const { chromium } = require('playwright');10(async () => {11  const browser = await chromium.launch();12  const page = await browser.newPage();13  await page.markAsSecure();14  await browser.close();15})();16Page.markAsAd()17const { chromium } = require('playwright');18(async () => {19  const browser = await chromium.launch();20  const page = await browser.newPage();21  await page.markAsAd();22  await browser.close();23})();24Page.setLifecycleEventsEnabled()25const { chromium } = require('playwright');26(async () => {27  const browser = await chromium.launch();28  const page = await browser.newPage();29  await page.setLifecycleEventsEnabled(true);30  await browser.close();31})();32Page.setFileChooserIntercepted()Using AI Code Generation
1const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');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 markRootUpdated(page);8  await page.screenshot({ path: 'example.png' });9  await browser.close();10})();11const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');12const { chromium } = require('playwright');13(async () => {14  const browser = await chromium.launch();15  const context = await browser.newContext();16  const page = await context.newPage();17  await markRootUpdated(page);18  await page.screenshot({ path: 'example.png' });19  await browser.close();20})();21const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');22const { chromium } = require('playwright');23(async () => {24  const browser = await chromium.launch();25  const context = await browser.newContext();26  const page = await context.newPage();27  await markRootUpdated(page);28  await page.screenshot({ path: 'example.png' });29  await browser.close();30})();31const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');32const { chromium } = require('playwright');33(async () => {34  const browser = await chromium.launch();35  const context = await browser.newContext();36  const page = await context.newPage();37  await markRootUpdated(page);38  await page.screenshot({ path: 'example.png' });39  await browser.close();40})();41const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');42const { chromium } = require('playwright');43(async () => {44  const browser = await chromium.launch();45  const context = await browser.newContext();Using AI Code Generation
1const { markRootUpdated } = require('playwright/lib/server/dom.js');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  const element = await page.$('input[name="q"]');8  await element.click();9  await page.keyboard.type('Hello World');10  await page.keyboard.press('Enter');11  await markRootUpdated(page);12  await page.screenshot({ path: 'google.png' });13  await browser.close();14})();15import { chromium } from 'playwright';16import { markRootUpdated } from 'playwright/lib/server/dom.js';17(async () => {18  const browser = await chromium.launch();19  const context = await browser.newContext();20  const page = await context.newPage();21  const element = await page.$('input[name="q"]');22  await element.click();23  await page.keyboard.type('Hello World');24  await page.keyboard.press('Enter');25  await markRootUpdated(page);26  await page.screenshot({ path: 'google.png' });27  await browser.close();28})();Using AI Code Generation
1const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');2markRootUpdated(page);3const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');4markRootUpdated(page);5const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');6markRootUpdated(page);7const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');8markRootUpdated(page);9const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');10markRootUpdated(page);11const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');12markRootUpdated(page);13const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');14markRootUpdated(page);15const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');16markRootUpdated(page);17const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');18markRootUpdated(page);19const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');20markRootUpdated(page);21const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');22markRootUpdated(page);23const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');24markRootUpdated(page);25const { markRootUpdatedUsing AI Code Generation
1const { markRootUpdated } = require('playwright/lib/server/domSnapshot');2const { createSnapshot } = require('playwright/lib/server/domSnapshot');3const { createDocument } = require('playwright/lib/server/domSnapshot');4const { createNode } = require('playwright/lib/server/domSnapshot');5const { createAttribute } = require('playwright/lib/server/domSnapshot');6const { createText } = require('playwright/lib/server/domSnapshot');7const { createComment } = require('playwright/lib/server/domSnapshot');8const document = createDocument();9const div = createNode('div', null, []);10const text = createText('text');11const comment = createComment('comment');12const attr = createAttribute('attr', 'value');13div.childNodes = [text, comment];14div.attributes = [attr];15document.root = div;16const snapshot = createSnapshot(document);17markRootUpdated(snapshot);18console.log(JSON.stringify(snapshot));19const { markRootUpdated } = require('playwright/lib/server/domSnapshot');20const { createSnapshot } = require('playwright/lib/server/domSnapshot');21const { createDocument } = require('playwright/lib/server/domSnapshot');22const { createNode } = require('playwright/lib/server/domSnapshot');23const { createAttribute } = require('playwright/lib/server/domSnapshot');24const { createText } = require('playwright/lib/server/domSnapshot');25const { createComment } = require('playwright/lib/server/domSnapshot');26const document = createDocument();27const div = createNode('div', null, []);28const text = createText('text');29const comment = createComment('comment');30const attr = createAttribute('attr', 'value');31div.childNodes = [text, comment];32div.attributes = [attr];33document.root = div;34const snapshot = createSnapshot(document);35markRootUpdated(snapshot);36console.log(JSON.stringify(snapshot));37const { markRootUpdated } = require('playwright/lib/server/domSnapshot');38const { createSnapshot } = require('playwright/lib/server/domSnapshot');39const { createDocument } = require('playwright/lib/server/domSnapshot');40const { createNode } = require('playwright/lib/server/domSnapshot');41const { createAttribute } = require('playwright/lib/server/domSnapshot');42const { createText } = require('playwright/lib/server/domSnapshot');43const { createComment } = require('playwright/lib/server/domSnapshot');Using AI Code Generation
1import { markRootUpdated } from "playwright/lib/server/inspector/inspector.js";2const playwright = require("playwright");3(async () => {4  const browser = await playwright["chromium"].launch();5  const context = await browser.newContext();6  const page = await context.newPage();7  await page.screenshot({ path: "example.png" });8  await browser.close();9})();Using AI Code Generation
1playwright._internal.markRootUpdated(document.body)2playwright._internal.markRootUpdated(document.body)3playwright._internal.markRootUpdated(document.body)4playwright._internal.markRootUpdated(document.body)5playwright._internal.markRootUpdated(document.body)6playwright._internal.markRootUpdated(document.body)7playwright._internal.markRootUpdated(document.body)8playwright._internal.markRootUpdated(document.body)9playwright._internal.markRootUpdated(document.body)10playwright._internal.markRootUpdated(document.body)11playwright._internal.markRootUpdated(document.body)12playwright._internal.markRootUpdated(document.body)Using AI Code Generation
1import { markRootUpdated } from '@playwright/internal';2markRootUpdated();3TypeError: (0, _playwright_internal.markRootUpdated) is not a function4I have imported the markRootUpdated method from the playwright/internal module. I don’t understand why I am getting this error. Can anyone help me with this?5@Shubham_Yadav, you need to import the module first. Like this:6import { markRootUpdated } from '@playwright/internal';7@Shubham_Yadav, you need to import the module first. Like this:8import { markRootUpdated } from '@playwright/internal';9@Shubham_Yadav, I see. I think you need to use the following import:10import { markRootUpdated } from '@playwright/test/lib/utils';11@Shubham_Yadav, I see. I think you need to use the following import:12import { markRootUpdated } from '@playwright/test/lib/utils';13import { markRootUpdated } from '@playwright/test/lib/utils';14markRootUpdated();15const { test } = require('@playwright/test');16test('my test', async ({ page }) => {17  try {18    await page.waitForSelector('not_existing_selector');19  } catch (e) {20    const { markRootUpdated } = require('@playwright/test/lib/utils');21    markRootUpdated();22    throw e;23  }24});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!!
