packages/react-reconciler/src/ReactFiberWorkLoop.js JAVASCRIPT 5,665 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 5,665.
1/**2 * Copyright (c) Meta Platforms, Inc. and affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @flow8 */910import {REACT_STRICT_MODE_TYPE} from 'shared/ReactSymbols';1112import type {13  Wakeable,14  Thenable,15  GestureOptionsRequired,16} from 'shared/ReactTypes';17import type {Fiber, FiberRoot} from './ReactInternalTypes';18import type {Lanes, Lane} from './ReactFiberLane';19import type {ActivityState} from './ReactFiberActivityComponent';20import type {SuspenseState} from './ReactFiberSuspenseComponent';21import type {FunctionComponentUpdateQueue} from './ReactFiberHooks';22import type {Transition} from 'react/src/ReactStartTransition';23import type {24  PendingTransitionCallbacks,25  PendingBoundaries,26  TransitionAbort,27} from './ReactFiberTracingMarkerComponent';28import type {OffscreenInstance} from './ReactFiberOffscreenComponent';29import type {30  Resource,31  ViewTransitionInstance,32  RunningViewTransition,33  GestureTimeline,34  SuspendedState,35} from './ReactFiberConfig';36import type {RootState} from './ReactFiberRoot';37import {38  getViewTransitionName,39  type ViewTransitionState,40} from './ReactFiberViewTransitionComponent';41import type {TransitionTypes} from 'react/src/ReactTransitionType';4243import {44  enableCreateEventHandleAPI,45  enableProfilerTimer,46  enableProfilerCommitHooks,47  enableProfilerNestedUpdatePhase,48  enableSchedulingProfiler,49  enableUpdaterTracking,50  enableTransitionTracing,51  disableLegacyContext,52  alwaysThrottleRetries,53  enableInfiniteRenderLoopDetection,54  enableInfiniteRenderLoopDetectionForceThrow,55  disableLegacyMode,56  enableComponentPerformanceTrack,57  enableYieldingBeforePassive,58  enableThrottledScheduling,59  enableViewTransition,60  enableGestureTransition,61  enableDefaultTransitionIndicator,62  enableParallelTransitions,63} from 'shared/ReactFeatureFlags';64import {resetOwnerStackLimit} from 'shared/ReactOwnerStackReset';65import ReactSharedInternals from 'shared/ReactSharedInternals';66import is from 'shared/objectIs';6768import reportGlobalError from 'shared/reportGlobalError';6970import {71  // Aliased because `act` will override and push to an internal queue72  scheduleCallback as Scheduler_scheduleCallback,73  shouldYield,74  requestPaint,75  now,76  NormalPriority as NormalSchedulerPriority,77  IdlePriority as IdleSchedulerPriority,78} from './Scheduler';79import {80  logBlockingStart,81  logGestureStart,82  logTransitionStart,83  logRenderPhase,84  logInterruptedRenderPhase,85  logSuspendedRenderPhase,86  logRecoveredRenderPhase,87  logErroredRenderPhase,88  logInconsistentRender,89  logSuspendedWithDelayPhase,90  logSuspendedCommitPhase,91  logSuspendedViewTransitionPhase,92  logCommitPhase,93  logPaintYieldPhase,94  logStartViewTransitionYieldPhase,95  logAnimatingPhase,96  logPassiveCommitPhase,97  logYieldTime,98  logActionYieldTime,99  logSuspendedYieldTime,100  setCurrentTrackFromLanes,101  markAllLanesInOrder,102  logApplyGesturePhase,103} from './ReactFiberPerformanceTrack';104105import {106  resetAfterCommit,107  scheduleTimeout,108  cancelTimeout,109  noTimeout,110  afterActiveInstanceBlur,111  startSuspendingCommit,112  suspendOnActiveViewTransition,113  waitForCommitToBeReady,114  getSuspendedCommitReason,115  preloadInstance,116  preloadResource,117  supportsHydration,118  setCurrentUpdatePriority,119  getCurrentUpdatePriority,120  resolveUpdatePriority,121  trackSchedulerEvent,122  startViewTransition,123  startGestureTransition,124  stopViewTransition,125  addViewTransitionFinishedListener,126  createViewTransitionInstance,127  flushHydrationEvents,128} from './ReactFiberConfig';129130import {createWorkInProgress, resetWorkInProgress} from './ReactFiber';131import {isRootDehydrated} from './ReactFiberShellHydration';132import {133  getIsHydrating,134  popHydrationStateOnInterruptedWork,135} from './ReactFiberHydrationContext';136import {137  NoMode,138  ProfileMode,139  ConcurrentMode,140  StrictLegacyMode,141  StrictEffectsMode,142} from './ReactTypeOfMode';143import {144  HostRoot,145  ClassComponent,146  ActivityComponent,147  SuspenseComponent,148  SuspenseListComponent,149  OffscreenComponent,150  FunctionComponent,151  ForwardRef,152  MemoComponent,153  SimpleMemoComponent,154  HostComponent,155  HostHoistable,156  HostSingleton,157} from './ReactWorkTags';158import {ConcurrentRoot, LegacyRoot} from './ReactRootTags';159import type {Flags} from './ReactFiberFlags';160import {161  NoFlags,162  Incomplete,163  StoreConsistency,164  HostEffectMask,165  ForceClientRender,166  BeforeMutationMask,167  MutationMask,168  LayoutMask,169  PassiveMask,170  PlacementDEV,171  Visibility,172  MountPassiveDev,173  MountLayoutDev,174  DidDefer,175  ShouldSuspendCommit,176  MaySuspendCommit,177  ScheduleRetry,178  PassiveTransitionMask,179} from './ReactFiberFlags';180import {181  NoLanes,182  NoLane,183  SyncLane,184  claimNextRetryLane,185  includesSyncLane,186  isSubsetOfLanes,187  mergeLanes,188  removeLanes,189  pickArbitraryLane,190  includesNonIdleWork,191  includesOnlyRetries,192  includesOnlyTransitions,193  includesBlockingLane,194  includesTransitionLane,195  includesRetryLane,196  includesIdleGroupLanes,197  includesExpiredLane,198  getNextLanes,199  getEntangledLanes,200  getLanesToRetrySynchronouslyOnError,201  upgradePendingLanesToSync,202  markRootSuspended as _markRootSuspended,203  markRootUpdated as _markRootUpdated,204  markRootPinged as _markRootPinged,205  markRootFinished,206  addFiberToLanesMap,207  movePendingFibersToMemoized,208  addTransitionToLanesMap,209  getTransitionsForLanes,210  includesSomeLane,211  OffscreenLane,212  SyncUpdateLanes,213  UpdateLanes,214  claimNextTransitionDeferredLane,215  checkIfRootIsPrerendering,216  includesOnlyViewTransitionEligibleLanes,217  isGestureRender,218  GestureLane,219  SomeTransitionLane,220  SomeRetryLane,221  IdleLane,222} from './ReactFiberLane';223import {224  DiscreteEventPriority,225  DefaultEventPriority,226  lowerEventPriority,227  lanesToEventPriority,228  eventPriorityToLane,229} from './ReactEventPriorities';230import {requestCurrentTransition} from './ReactFiberTransition';231import {232  SelectiveHydrationException,233  beginWork,234  replayFunctionComponent,235} from './ReactFiberBeginWork';236import {completeWork} from './ReactFiberCompleteWork';237import {unwindWork, unwindInterruptedWork} from './ReactFiberUnwindWork';238import {239  throwException,240  createRootErrorUpdate,241  createClassErrorUpdate,242  initializeClassErrorUpdate,243} from './ReactFiberThrow';244import {245  commitBeforeMutationEffects,246  shouldFireAfterActiveInstanceBlur,247  commitAfterMutationEffects,248  commitLayoutEffects,249  commitMutationEffects,250  commitPassiveMountEffects,251  commitPassiveUnmountEffects,252  disappearLayoutEffects,253  reconnectPassiveEffects,254  reappearLayoutEffects,255  disconnectPassiveEffect,256  invokeLayoutEffectMountInDEV,257  invokePassiveEffectMountInDEV,258  invokeLayoutEffectUnmountInDEV,259  invokePassiveEffectUnmountInDEV,260  accumulateSuspenseyCommit,261} from './ReactFiberCommitWork';262import {resetShouldStartViewTransition} from './ReactFiberCommitViewTransitions';263import {shouldStartViewTransition} from './ReactFiberCommitViewTransitions';264import {265  insertDestinationClones,266  applyDepartureTransitions,267  startGestureAnimations,268} from './ReactFiberApplyGesture';269import {enqueueUpdate} from './ReactFiberClassUpdateQueue';270import {resetContextDependencies} from './ReactFiberNewContext';271import {272  resetHooksAfterThrow,273  resetHooksOnUnwind,274  ContextOnlyDispatcher,275} from './ReactFiberHooks';276import {DefaultAsyncDispatcher} from './ReactFiberAsyncDispatcher';277import {278  createCapturedValueAtFiber,279  type CapturedValue,280} from './ReactCapturedValue';281import {282  enqueueConcurrentRenderForLane,283  finishQueueingConcurrentUpdates,284  getConcurrentlyUpdatedLanes,285} from './ReactFiberConcurrentUpdates';286287import {288  blockingClampTime,289  blockingUpdateTime,290  blockingUpdateTask,291  blockingUpdateType,292  blockingUpdateMethodName,293  blockingUpdateComponentName,294  blockingEventTime,295  blockingEventType,296  blockingEventRepeatTime,297  blockingSuspendedTime,298  gestureClampTime,299  gestureUpdateTime,300  gestureUpdateTask,301  gestureUpdateType,302  gestureUpdateMethodName,303  gestureUpdateComponentName,304  gestureEventTime,305  gestureEventType,306  gestureEventRepeatTime,307  gestureSuspendedTime,308  transitionClampTime,309  transitionStartTime,310  transitionUpdateTime,311  transitionUpdateTask,312  transitionUpdateType,313  transitionUpdateMethodName,314  transitionUpdateComponentName,315  transitionEventTime,316  transitionEventType,317  transitionEventRepeatTime,318  transitionSuspendedTime,319  clearBlockingTimers,320  clearGestureTimers,321  clearGestureUpdates,322  clearTransitionTimers,323  clampBlockingTimers,324  clampGestureTimers,325  clampTransitionTimers,326  clampRetryTimers,327  clampIdleTimers,328  markNestedUpdateScheduled,329  renderStartTime,330  commitStartTime,331  commitEndTime,332  commitErrors,333  recordRenderTime,334  recordCommitTime,335  recordCommitEndTime,336  startProfilerTimer,337  stopProfilerTimerIfRunningAndRecordDuration,338  stopProfilerTimerIfRunningAndRecordIncompleteDuration,339  trackSuspendedTime,340  startYieldTimer,341  yieldStartTime,342  yieldReason,343  startPingTimerByLanes,344  recordEffectError,345  resetCommitErrors,346  PINGED_UPDATE,347  SPAWNED_UPDATE,348  startAnimating,349  stopAnimating,350  animatingLanes,351  retryClampTime,352  idleClampTime,353  animatingTask,354} from './ReactProfilerTimer';355356// DEV stuff357import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';358import ReactStrictModeWarnings from './ReactStrictModeWarnings';359import {360  isRendering as ReactCurrentDebugFiberIsRenderingInDEV,361  resetCurrentFiber,362  runWithFiberInDEV,363} from './ReactCurrentFiber';364import {365  isDevToolsPresent,366  markCommitStarted,367  markCommitStopped,368  markComponentRenderStopped,369  markComponentSuspended,370  markComponentErrored,371  markLayoutEffectsStarted,372  markLayoutEffectsStopped,373  markPassiveEffectsStarted,374  markPassiveEffectsStopped,375  markRenderStarted,376  markRenderYielded,377  markRenderStopped,378  onCommitRoot as onCommitRootDevTools,379  onPostCommitRoot as onPostCommitRootDevTools,380  setIsStrictModeForDevtools,381} from './ReactFiberDevToolsHook';382import {onCommitRoot as onCommitRootTestSelector} from './ReactTestSelectors';383import {releaseCache} from './ReactFiberCacheComponent';384import {385  isLegacyActEnvironment,386  isConcurrentActEnvironment,387} from './ReactFiberAct';388import {processTransitionCallbacks} from './ReactFiberTracingMarkerComponent';389import {390  SuspenseException,391  SuspenseActionException,392  SuspenseyCommitException,393  getSuspendedThenable,394  isThenableResolved,395} from './ReactFiberThenable';396import {schedulePostPaintCallback} from './ReactPostPaintCallback';397import {398  getSuspenseHandler,399  getShellBoundary,400} from './ReactFiberSuspenseContext';401import {resetChildReconcilerOnUnwind} from './ReactChildFiber';402import {403  ensureRootIsScheduled,404  flushSyncWorkOnAllRoots,405  flushSyncWorkOnLegacyRootsOnly,406  requestTransitionLane,407} from './ReactFiberRootScheduler';408import {getMaskedContext, getUnmaskedContext} from './ReactFiberLegacyContext';409import {logUncaughtError} from './ReactFiberErrorLogger';410import {411  scheduleGestureCommit,412  stopCommittedGesture,413} from './ReactFiberGestureScheduler';414import {claimQueuedTransitionTypes} from './ReactFiberTransitionTypes';415416const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;417418type ExecutionContext = number;419420export const NoContext = /*             */ 0b000;421const BatchedContext = /*               */ 0b001;422export const RenderContext = /*         */ 0b010;423export const CommitContext = /*         */ 0b100;424425type RootExitStatus = 0 | 1 | 2 | 3 | 4 | 5 | 6;426const RootInProgress = 0;427const RootFatalErrored = 1;428const RootErrored = 2;429const RootSuspended = 3;430const RootSuspendedWithDelay = 4;431const RootSuspendedAtTheShell = 6;432const RootCompleted = 5;433434// Describes where we are in the React execution stack435let executionContext: ExecutionContext = NoContext;436// The root we're working on437let workInProgressRoot: FiberRoot | null = null;438// The fiber we're working on439let workInProgress: Fiber | null = null;440// The lanes we're rendering441let workInProgressRootRenderLanes: Lanes = NoLanes;442443export opaque type SuspendedReason = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;444const NotSuspended: SuspendedReason = 0;445const SuspendedOnError: SuspendedReason = 1;446const SuspendedOnData: SuspendedReason = 2;447const SuspendedOnImmediate: SuspendedReason = 3;448const SuspendedOnInstance: SuspendedReason = 4;449const SuspendedOnInstanceAndReadyToContinue: SuspendedReason = 5;450const SuspendedOnDeprecatedThrowPromise: SuspendedReason = 6;451const SuspendedAndReadyToContinue: SuspendedReason = 7;452const SuspendedOnHydration: SuspendedReason = 8;453const SuspendedOnAction: SuspendedReason = 9;454455// When this is true, the work-in-progress fiber just suspended (or errored) and456// we've yet to unwind the stack. In some cases, we may yield to the main thread457// after this happens. If the fiber is pinged before we resume, we can retry458// immediately instead of unwinding the stack.459let workInProgressSuspendedReason: SuspendedReason = NotSuspended;460let workInProgressThrownValue: mixed = null;461462// Tracks whether any siblings were skipped during the unwind phase after463// something suspends. Used to determine whether to schedule another render464// to prewarm the skipped siblings.465let workInProgressRootDidSkipSuspendedSiblings: boolean = false;466// Whether the work-in-progress render is the result of a prewarm/prerender.467// This tells us whether or not we should render the siblings after468// something suspends.469let workInProgressRootIsPrerendering: boolean = false;470471// Whether a ping listener was attached during this render. This is slightly472// different that whether something suspended, because we don't add multiple473// listeners to a promise we've already seen (per root and lane).474let workInProgressRootDidAttachPingListener: boolean = false;475476// A contextual version of workInProgressRootRenderLanes. It is a superset of477// the lanes that we started working on at the root. When we enter a subtree478// that is currently hidden, we add the lanes that would have committed if479// the hidden tree hadn't been deferred. This is modified by the480// HiddenContext module.481//482// Most things in the work loop should deal with workInProgressRootRenderLanes.483// Most things in begin/complete phases should deal with entangledRenderLanes.484export let entangledRenderLanes: Lanes = NoLanes;485486// Whether to root completed, errored, suspended, etc.487let workInProgressRootExitStatus: RootExitStatus = RootInProgress;488// The work left over by components that were visited during this render. Only489// includes unprocessed updates, not work in bailed out children.490let workInProgressRootSkippedLanes: Lanes = NoLanes;491// Lanes that were updated (in an interleaved event) during this render.492let workInProgressRootInterleavedUpdatedLanes: Lanes = NoLanes;493// Lanes that were updated during the render phase (*not* an interleaved event).494let workInProgressRootRenderPhaseUpdatedLanes: Lanes = NoLanes;495// Lanes that were pinged (in an interleaved event) during this render.496let workInProgressRootPingedLanes: Lanes = NoLanes;497// If this render scheduled deferred work, this is the lane of the deferred task.498let workInProgressDeferredLane: Lane = NoLane;499// Represents the retry lanes that were spawned by this render and have not500// been pinged since, implying that they are still suspended.501let workInProgressSuspendedRetryLanes: Lanes = NoLanes;502// Errors that are thrown during the render phase.503let workInProgressRootConcurrentErrors: Array<CapturedValue<mixed>> | null =504  null;505// These are errors that we recovered from without surfacing them to the UI.506// We will log them once the tree commits.507let workInProgressRootRecoverableErrors: Array<CapturedValue<mixed>> | null =508  null;509510// Tracks when an update occurs during the render phase.511let workInProgressRootDidIncludeRecursiveRenderUpdate: boolean = false;512// Thacks when an update occurs during the commit phase. It's a separate513// variable from the one for renders because the commit phase may run514// concurrently to a render phase.515let didIncludeCommitPhaseUpdate: boolean = false;516// The most recent time we either committed a fallback, or when a fallback was517// filled in with the resolved UI. This lets us throttle the appearance of new518// content as it streams in, to minimize jank.519// TODO: Think of a better name for this variable?520let globalMostRecentFallbackTime: number = 0;521// Track the most recent time we started a new Transition. This lets us apply522// heuristics like the suspensey image timeout based on how long we've waited523// already.524let globalMostRecentTransitionTime: number = 0;525526const FALLBACK_THROTTLE_MS: number = 300;527528// The absolute time for when we should start giving up on rendering529// more and prefer CPU suspense heuristics instead.530let workInProgressRootRenderTargetTime: number = Infinity;531// How long a render is supposed to take before we start following CPU532// suspense heuristics and opt out of rendering more content.533const RENDER_TIMEOUT_MS = 500;534535let workInProgressTransitions: Array<Transition> | null = null;536export function getWorkInProgressTransitions(): null | Array<Transition> {537  return workInProgressTransitions;538}539540// The first setState call that eventually caused the current render.541let workInProgressUpdateTask: null | ConsoleTask = null;542543let currentPendingTransitionCallbacks: PendingTransitionCallbacks | null = null;544let currentEndTime: number | null = null;545546export function addTransitionStartCallbackToPendingTransition(547  transition: Transition,548) {549  if (enableTransitionTracing) {550    if (currentPendingTransitionCallbacks === null) {551      currentPendingTransitionCallbacks = {552        transitionStart: [],553        transitionProgress: null,554        transitionComplete: null,555        markerProgress: null,556        markerIncomplete: null,557        markerComplete: null,558      };559    }560561    if (currentPendingTransitionCallbacks.transitionStart === null) {562      currentPendingTransitionCallbacks.transitionStart =563        [] as Array<Transition>;564    }565566    currentPendingTransitionCallbacks.transitionStart.push(transition);567  }568}569570export function addMarkerProgressCallbackToPendingTransition(571  markerName: string,572  transitions: Set<Transition>,573  pendingBoundaries: PendingBoundaries,574) {575  if (enableTransitionTracing) {576    if (currentPendingTransitionCallbacks === null) {577      currentPendingTransitionCallbacks = {578        transitionStart: null,579        transitionProgress: null,580        transitionComplete: null,581        markerProgress: new Map(),582        markerIncomplete: null,583        markerComplete: null,584      } as PendingTransitionCallbacks;585    }586587    if (currentPendingTransitionCallbacks.markerProgress === null) {588      currentPendingTransitionCallbacks.markerProgress = new Map();589    }590591    currentPendingTransitionCallbacks.markerProgress.set(markerName, {592      pendingBoundaries,593      transitions,594    });595  }596}597598export function addMarkerIncompleteCallbackToPendingTransition(599  markerName: string,600  transitions: Set<Transition>,601  aborts: Array<TransitionAbort>,602) {603  if (enableTransitionTracing) {604    if (currentPendingTransitionCallbacks === null) {605      currentPendingTransitionCallbacks = {606        transitionStart: null,607        transitionProgress: null,608        transitionComplete: null,609        markerProgress: null,610        markerIncomplete: new Map(),611        markerComplete: null,612      };613    }614615    if (currentPendingTransitionCallbacks.markerIncomplete === null) {616      currentPendingTransitionCallbacks.markerIncomplete = new Map();617    }618619    currentPendingTransitionCallbacks.markerIncomplete.set(markerName, {620      transitions,621      aborts,622    });623  }624}625626export function addMarkerCompleteCallbackToPendingTransition(627  markerName: string,628  transitions: Set<Transition>,629) {630  if (enableTransitionTracing) {631    if (currentPendingTransitionCallbacks === null) {632      currentPendingTransitionCallbacks = {633        transitionStart: null,634        transitionProgress: null,635        transitionComplete: null,636        markerProgress: null,637        markerIncomplete: null,638        markerComplete: new Map(),639      };640    }641642    if (currentPendingTransitionCallbacks.markerComplete === null) {643      currentPendingTransitionCallbacks.markerComplete = new Map();644    }645646    currentPendingTransitionCallbacks.markerComplete.set(647      markerName,648      transitions,649    );650  }651}652653export function addTransitionProgressCallbackToPendingTransition(654  transition: Transition,655  boundaries: PendingBoundaries,656) {657  if (enableTransitionTracing) {658    if (currentPendingTransitionCallbacks === null) {659      currentPendingTransitionCallbacks = {660        transitionStart: null,661        transitionProgress: new Map(),662        transitionComplete: null,663        markerProgress: null,664        markerIncomplete: null,665        markerComplete: null,666      };667    }668669    if (currentPendingTransitionCallbacks.transitionProgress === null) {670      currentPendingTransitionCallbacks.transitionProgress = new Map();671    }672673    currentPendingTransitionCallbacks.transitionProgress.set(674      transition,675      boundaries,676    );677  }678}679680export function addTransitionCompleteCallbackToPendingTransition(681  transition: Transition,682) {683  if (enableTransitionTracing) {684    if (currentPendingTransitionCallbacks === null) {685      currentPendingTransitionCallbacks = {686        transitionStart: null,687        transitionProgress: null,688        transitionComplete: [],689        markerProgress: null,690        markerIncomplete: null,691        markerComplete: null,692      };693    }694695    if (currentPendingTransitionCallbacks.transitionComplete === null) {696      currentPendingTransitionCallbacks.transitionComplete =697        [] as Array<Transition>;698    }699700    currentPendingTransitionCallbacks.transitionComplete.push(transition);701  }702}703704function resetRenderTimer() {705  workInProgressRootRenderTargetTime = now() + RENDER_TIMEOUT_MS;706}707708export function getRenderTargetTime(): number {709  return workInProgressRootRenderTargetTime;710}711712let legacyErrorBoundariesThatAlreadyFailed: Set<mixed> | null = null;713714type SuspendedCommitReason = null | string;715716type DelayedCommitReason = 0 | 1 | 2 | 3;717const IMMEDIATE_COMMIT = 0;718const ABORTED_VIEW_TRANSITION_COMMIT = 1;719const DELAYED_PASSIVE_COMMIT = 2;720const ANIMATION_STARTED_COMMIT = 3;721722const NO_PENDING_EFFECTS = 0;723const PENDING_MUTATION_PHASE = 1;724const PENDING_LAYOUT_PHASE = 2;725const PENDING_AFTER_MUTATION_PHASE = 3;726const PENDING_SPAWNED_WORK = 4;727const PENDING_PASSIVE_PHASE = 5;728const PENDING_GESTURE_MUTATION_PHASE = 6;729const PENDING_GESTURE_ANIMATION_PHASE = 7;730let pendingEffectsStatus: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 = 0;731let pendingEffectsRoot: FiberRoot = null as any;732let pendingFinishedWork: Fiber = null as any;733let pendingEffectsLanes: Lanes = NoLanes;734let pendingEffectsRemainingLanes: Lanes = NoLanes;735let pendingEffectsRenderEndTime: number = -0; // Profiling-only736let pendingPassiveTransitions: Array<Transition> | null = null;737let pendingRecoverableErrors: null | Array<CapturedValue<mixed>> = null;738let pendingViewTransition: null | RunningViewTransition = null;739let pendingViewTransitionEvents: Array<740  (types: Array<string>) => void | (() => void),741> | null = null;742let pendingTransitionTypes: null | TransitionTypes = null;743let pendingDidIncludeRenderPhaseUpdate: boolean = false;744let pendingSuspendedCommitReason: SuspendedCommitReason = null; // Profiling-only745let pendingDelayedCommitReason: DelayedCommitReason = IMMEDIATE_COMMIT; // Profiling-only746let pendingSuspendedViewTransitionReason: null | string = null; // Profiling-only747748// Use these to prevent an infinite loop of nested updates749const NESTED_UPDATE_LIMIT = 50;750let nestedUpdateCount: number = 0;751let rootWithNestedUpdates: FiberRoot | null = null;752let isFlushingPassiveEffects = false;753let didScheduleUpdateDuringPassiveEffects = false;754755const NO_NESTED_UPDATE = 0;756const NESTED_UPDATE_SYNC_LANE = 1;757const NESTED_UPDATE_PHASE_SPAWN = 2;758let nestedUpdateKind: 0 | 1 | 2 = NO_NESTED_UPDATE;759760const NESTED_PASSIVE_UPDATE_LIMIT = 50;761let nestedPassiveUpdateCount: number = 0;762let rootWithPassiveNestedUpdates: FiberRoot | null = null;763764let isRunningInsertionEffect = false;765766export function getWorkInProgressRoot(): FiberRoot | null {767  return workInProgressRoot;768}769770export function getCommittingRoot(): FiberRoot | null {771  return pendingEffectsRoot;772}773774export function getWorkInProgressRootRenderLanes(): Lanes {775  return workInProgressRootRenderLanes;776}777778export function hasPendingCommitEffects(): boolean {779  return (780    pendingEffectsStatus !== NO_PENDING_EFFECTS &&781    pendingEffectsStatus !== PENDING_PASSIVE_PHASE782  );783}784785export function getRootWithPendingPassiveEffects(): FiberRoot | null {786  return pendingEffectsStatus === PENDING_PASSIVE_PHASE787    ? pendingEffectsRoot788    : null;789}790791export function getPendingPassiveEffectsLanes(): Lanes {792  return pendingEffectsLanes;793}794795export function getPendingTransitionTypes(): null | TransitionTypes {796  return pendingTransitionTypes;797}798799export function isWorkLoopSuspendedOnData(): boolean {800  return (801    workInProgressSuspendedReason === SuspendedOnData ||802    workInProgressSuspendedReason === SuspendedOnAction803  );804}805806export function getCurrentTime(): number {807  return now();808}809810export function requestUpdateLane(fiber: Fiber): Lane {811  // Special cases812  const mode = fiber.mode;813  if (!disableLegacyMode && (mode & ConcurrentMode) === NoMode) {814    return SyncLane as Lane;815  } else if (816    (executionContext & RenderContext) !== NoContext &&817    workInProgressRootRenderLanes !== NoLanes818  ) {819    // This is a render phase update. These are not officially supported. The820    // old behavior is to give this the same "thread" (lanes) as821    // whatever is currently rendering. So if you call `setState` on a component822    // that happens later in the same render, it will flush. Ideally, we want to823    // remove the special case and treat them as if they came from an824    // interleaved event. Regardless, this pattern is not officially supported.825    // This behavior is only a fallback. The flag only exists until we can roll826    // out the setState warning, since existing code might accidentally rely on827    // the current behavior.828    return pickArbitraryLane(workInProgressRootRenderLanes);829  }830831  const transition = requestCurrentTransition();832  if (transition !== null) {833    if (enableGestureTransition) {834      if (transition.gesture) {835        throw new Error(836          'Cannot setState on regular state inside a startGestureTransition. ' +837            'Gestures can only update the useOptimistic() hook. There should be no ' +838            'side-effects associated with starting a Gesture until its Action is ' +839            'invoked. Move side-effects to the Action instead.',840        );841      }842    }843    if (__DEV__) {844      if (!transition._updatedFibers) {845        transition._updatedFibers = new Set();846      }847      transition._updatedFibers.add(fiber);848    }849850    return requestTransitionLane(transition);851  }852853  return eventPriorityToLane(resolveUpdatePriority());854}855856function requestRetryLane(fiber: Fiber) {857  // This is a fork of `requestUpdateLane` designed specifically for Suspense858  // "retries" — a special update that attempts to flip a Suspense boundary859  // from its placeholder state to its primary/resolved state.860861  // Special cases862  const mode = fiber.mode;863  if (!disableLegacyMode && (mode & ConcurrentMode) === NoMode) {864    return SyncLane as Lane;865  }866867  return claimNextRetryLane();868}869870export function requestDeferredLane(): Lane {871  if (workInProgressDeferredLane === NoLane) {872    // If there are multiple useDeferredValue hooks in the same render, the873    // tasks that they spawn should all be batched together, so they should all874    // receive the same lane.875876    // Check the priority of the current render to decide the priority of the877    // deferred task.878879    // OffscreenLane is used for prerendering, but we also use OffscreenLane880    // for incremental hydration. It's given the lowest priority because the881    // initial HTML is the same as the final UI. But useDeferredValue during882    // hydration is an exception — we need to upgrade the UI to the final883    // value. So if we're currently hydrating, we treat it like a transition.884    const isPrerendering =885      includesSomeLane(workInProgressRootRenderLanes, OffscreenLane) &&886      !getIsHydrating();887    if (isPrerendering) {888      // There's only one OffscreenLane, so if it contains deferred work, we889      // should just reschedule using the same lane.890      workInProgressDeferredLane = OffscreenLane;891    } else {892      // Everything else is spawned as a transition.893      workInProgressDeferredLane = claimNextTransitionDeferredLane();894    }895  }896897  // Mark the parent Suspense boundary so it knows to spawn the deferred lane.898  const suspenseHandler = getSuspenseHandler();899  if (suspenseHandler !== null) {900    // TODO: As an optimization, we shouldn't entangle the lanes at the root; we901    // can entangle them using the baseLanes of the Suspense boundary instead.902    // We only need to do something special if there's no Suspense boundary.903    suspenseHandler.flags |= DidDefer;904  }905906  return workInProgressDeferredLane;907}908909export function scheduleViewTransitionEvent(910  fiber: Fiber,911  callback: ?(912    instance: ViewTransitionInstance,913    types: Array<string>,914  ) => void | (() => void),915): void {916  if (enableViewTransition) {917    if (callback != null) {918      const state: ViewTransitionState = fiber.stateNode;919      let instance = state.ref;920      if (instance === null) {921        instance = state.ref = createViewTransitionInstance(922          getViewTransitionName(fiber.memoizedProps, state),923        );924      }925      if (pendingViewTransitionEvents === null) {926        pendingViewTransitionEvents = [];927      }928      pendingViewTransitionEvents.push(callback.bind(null, instance));929    }930  }931}932933export function scheduleGestureTransitionEvent(934  fiber: Fiber,935  callback: ?(936    timeline: GestureTimeline,937    options: GestureOptionsRequired,938    instance: ViewTransitionInstance,939    types: Array<string>,940  ) => void | (() => void),941): void {942  if (enableGestureTransition) {943    if (callback != null) {944      const applyingGesture = pendingEffectsRoot.pendingGestures;945      if (applyingGesture !== null) {946        const state: ViewTransitionState = fiber.stateNode;947        let instance = state.ref;948        if (instance === null) {949          instance = state.ref = createViewTransitionInstance(950            getViewTransitionName(fiber.memoizedProps, state),951          );952        }953        const timeline = applyingGesture.provider;954        const options = {955          rangeStart: applyingGesture.rangeStart,956          rangeEnd: applyingGesture.rangeEnd,957        };958        if (pendingViewTransitionEvents === null) {959          pendingViewTransitionEvents = [];960        }961        pendingViewTransitionEvents.push(962          callback.bind(null, timeline, options, instance),963        );964      }965    }966  }967}968969export function peekDeferredLane(): Lane {970  return workInProgressDeferredLane;971}972973export function scheduleUpdateOnFiber(974  root: FiberRoot,975  fiber: Fiber,976  lane: Lane,977) {978  if (__DEV__) {979    if (isRunningInsertionEffect) {980      console.error('useInsertionEffect must not schedule updates.');981    }982  }983984  if (__DEV__) {985    if (isFlushingPassiveEffects) {986      didScheduleUpdateDuringPassiveEffects = true;987    }988  }989990  // Check if the work loop is currently suspended and waiting for data to991  // finish loading.992  if (993    // Suspended render phase994    (root === workInProgressRoot &&995      (workInProgressSuspendedReason === SuspendedOnData ||996        workInProgressSuspendedReason === SuspendedOnAction)) ||997    // Suspended commit phase998    root.cancelPendingCommit !== null999  ) {1000    // The incoming update might unblock the current render. Interrupt the1001    // current attempt and restart from the top.1002    prepareFreshStack(root, NoLanes);1003    const didAttemptEntireTree = false;1004    markRootSuspended(1005      root,1006      workInProgressRootRenderLanes,1007      workInProgressDeferredLane,1008      didAttemptEntireTree,1009    );1010  }10111012  // Mark that the root has a pending update.1013  markRootUpdated(root, lane);10141015  if (1016    (executionContext & RenderContext) !== NoContext &&1017    root === workInProgressRoot1018  ) {1019    // This update was dispatched during the render phase. This is a mistake1020    // if the update originates from user space (with the exception of local1021    // hook updates, which are handled differently and don't reach this1022    // function), but there are some internal React features that use this as1023    // an implementation detail, like selective hydration.1024    warnAboutRenderPhaseUpdatesInDEV(fiber);10251026    // Track lanes that were updated during the render phase1027    workInProgressRootRenderPhaseUpdatedLanes = mergeLanes(1028      workInProgressRootRenderPhaseUpdatedLanes,1029      lane,1030    );1031  } else {1032    // This is a normal update, scheduled from outside the render phase. For1033    // example, during an input event.1034    if (enableUpdaterTracking) {1035      if (isDevToolsPresent) {1036        addFiberToLanesMap(root, fiber, lane);1037      }1038    }10391040    warnIfUpdatesNotWrappedWithActDEV(fiber);10411042    if (enableTransitionTracing) {1043      const transition = ReactSharedInternals.T;1044      if (transition !== null && transition.name != null) {1045        if (transition.startTime === -1) {1046          transition.startTime = now();1047        }10481049        addTransitionToLanesMap(root, transition, lane);1050      }1051    }10521053    if (root === workInProgressRoot) {1054      // Received an update to a tree that's in the middle of rendering. Mark1055      // that there was an interleaved update work on this root.1056      if ((executionContext & RenderContext) === NoContext) {1057        workInProgressRootInterleavedUpdatedLanes = mergeLanes(1058          workInProgressRootInterleavedUpdatedLanes,1059          lane,1060        );1061      }1062      if (workInProgressRootExitStatus === RootSuspendedWithDelay) {1063        // The root already suspended with a delay, which means this render1064        // definitely won't finish. Since we have a new update, let's mark it as1065        // suspended now, right before marking the incoming update. This has the1066        // effect of interrupting the current render and switching to the update.1067        // TODO: Make sure this doesn't override pings that happen while we've1068        // already started rendering.1069        const didAttemptEntireTree = false;1070        markRootSuspended(1071          root,1072          workInProgressRootRenderLanes,1073          workInProgressDeferredLane,1074          didAttemptEntireTree,1075        );1076      }1077    }10781079    ensureRootIsScheduled(root);1080    if (1081      lane === SyncLane &&1082      executionContext === NoContext &&1083      !disableLegacyMode &&1084      (fiber.mode & ConcurrentMode) === NoMode1085    ) {1086      if (__DEV__ && ReactSharedInternals.isBatchingLegacy) {1087        // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.1088      } else {1089        // Flush the synchronous work now, unless we're already working or inside1090        // a batch. This is intentionally inside scheduleUpdateOnFiber instead of1091        // scheduleCallbackForFiber to preserve the ability to schedule a callback1092        // without immediately flushing it. We only do this for user-initiated1093        // updates, to preserve historical behavior of legacy mode.1094        resetRenderTimer();1095        flushSyncWorkOnLegacyRootsOnly();1096      }1097    }1098  }1099}11001101export function scheduleInitialHydrationOnRoot(root: FiberRoot, lane: Lane) {1102  // This is a special fork of scheduleUpdateOnFiber that is only used to1103  // schedule the initial hydration of a root that has just been created. Most1104  // of the stuff in scheduleUpdateOnFiber can be skipped.1105  //1106  // The main reason for this separate path, though, is to distinguish the1107  // initial children from subsequent updates. In fully client-rendered roots1108  // (createRoot instead of hydrateRoot), all top-level renders are modeled as1109  // updates, but hydration roots are special because the initial render must1110  // match what was rendered on the server.1111  const current = root.current;1112  current.lanes = lane;1113  markRootUpdated(root, lane);1114  ensureRootIsScheduled(root);1115}11161117export function isUnsafeClassRenderPhaseUpdate(fiber: Fiber): boolean {1118  // Check if this is a render phase update. Only called by class components,1119  // which special (deprecated) behavior for UNSAFE_componentWillReceive props.1120  return (executionContext & RenderContext) !== NoContext;1121}11221123export function performWorkOnRoot(1124  root: FiberRoot,1125  lanes: Lanes,1126  forceSync: boolean,1127): void {1128  if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {1129    throw new Error('Should not already be working.');1130  }11311132  if (enableProfilerTimer && enableComponentPerformanceTrack) {1133    if (workInProgressRootRenderLanes !== NoLanes && workInProgress !== null) {1134      const yieldedFiber = workInProgress;1135      // We've returned from yielding to the event loop. Let's log the time it took.1136      const yieldEndTime = now();1137      switch (yieldReason) {1138        case SuspendedOnImmediate:1139        case SuspendedOnData:1140          logSuspendedYieldTime(yieldStartTime, yieldEndTime, yieldedFiber);1141          break;1142        case SuspendedOnAction:1143          logActionYieldTime(yieldStartTime, yieldEndTime, yieldedFiber);1144          break;1145        default:1146          logYieldTime(yieldStartTime, yieldEndTime);1147      }1148    }1149  }11501151  // We disable time-slicing in some cases: if the work has been CPU-bound1152  // for too long ("expired" work, to prevent starvation), or we're in1153  // sync-updates-by-default mode.1154  const shouldTimeSlice =1155    (!forceSync &&1156      !includesBlockingLane(lanes) &&1157      !includesExpiredLane(root, lanes)) ||1158    // If we're prerendering, then we should use the concurrent work loop1159    // even if the lanes are synchronous, so that prerendering never blocks1160    // the main thread.1161    // TODO: We should consider doing this whenever a sync lane is suspended,1162    // even for regular pings.1163    checkIfRootIsPrerendering(root, lanes);11641165  let exitStatus: RootExitStatus = shouldTimeSlice1166    ? renderRootConcurrent(root, lanes)1167    : renderRootSync(root, lanes, true);11681169  let renderWasConcurrent = shouldTimeSlice;11701171  do {1172    if (exitStatus === RootInProgress) {1173      // Render phase is still in progress.1174      if (workInProgressRootIsPrerendering && !shouldTimeSlice) {1175        // We're in prerendering mode, but time slicing is not enabled. This1176        // happens when something suspends during a synchronous update. Exit the1177        // the work loop. When we resume, we'll use the concurrent work loop so1178        // that prerendering is non-blocking.1179        //1180        // Mark the root as suspended. Usually we do this at the end of the1181        // render phase, but we do it here so that we resume in1182        // prerendering mode.1183        // TODO: Consider always calling markRootSuspended immediately.1184        // Needs to be *after* we attach a ping listener, though.1185        const didAttemptEntireTree = false;1186        markRootSuspended(root, lanes, NoLane, didAttemptEntireTree);1187      }1188      if (enableProfilerTimer && enableComponentPerformanceTrack) {1189        // We're about to yield. Let's keep track of how long we yield to the event loop.1190        // We also stash the suspended reason at the time we yielded since it might have1191        // changed when we resume such as when it gets pinged.1192        startYieldTimer(workInProgressSuspendedReason);1193      }1194      break;1195    } else {1196      let renderEndTime = 0;1197      if (enableProfilerTimer && enableComponentPerformanceTrack) {1198        renderEndTime = now();1199      }12001201      // The render completed.12021203      // Check if this render may have yielded to a concurrent event, and if so,1204      // confirm that any newly rendered stores are consistent.1205      // TODO: It's possible that even a concurrent render may never have yielded1206      // to the main thread, if it was fast enough, or if it expired. We could1207      // skip the consistency check in that case, too.1208      const finishedWork: Fiber = root.current.alternate as any;1209      if (1210        renderWasConcurrent &&1211        !isRenderConsistentWithExternalStores(finishedWork)1212      ) {1213        if (enableProfilerTimer && enableComponentPerformanceTrack) {1214          setCurrentTrackFromLanes(lanes);1215          logInconsistentRender(1216            renderStartTime,1217            renderEndTime,1218            workInProgressUpdateTask,1219          );1220          finalizeRender(lanes, renderEndTime);1221        }1222        // A store was mutated in an interleaved event. Render again,1223        // synchronously, to block further mutations.1224        exitStatus = renderRootSync(root, lanes, false);1225        // We assume the tree is now consistent because we didn't yield to any1226        // concurrent events.1227        renderWasConcurrent = false;1228        // Need to check the exit status again.1229        continue;1230      }12311232      // Check if something threw1233      if (1234        (disableLegacyMode || root.tag !== LegacyRoot) &&1235        exitStatus === RootErrored1236      ) {1237        const lanesThatJustErrored = lanes;1238        const errorRetryLanes = getLanesToRetrySynchronouslyOnError(1239          root,1240          lanesThatJustErrored,1241        );1242        if (errorRetryLanes !== NoLanes) {1243          if (enableProfilerTimer && enableComponentPerformanceTrack) {1244            setCurrentTrackFromLanes(lanes);1245            logErroredRenderPhase(1246              renderStartTime,1247              renderEndTime,1248              lanes,1249              workInProgressUpdateTask,1250            );1251            finalizeRender(lanes, renderEndTime);1252          }1253          lanes = errorRetryLanes;1254          exitStatus = recoverFromConcurrentError(1255            root,1256            lanesThatJustErrored,1257            errorRetryLanes,1258          );1259          renderWasConcurrent = false;1260          // Need to check the exit status again.1261          if (exitStatus !== RootErrored) {1262            // The root did not error this time. Restart the exit algorithm1263            // from the beginning.1264            // TODO: Refactor the exit algorithm to be less confusing. Maybe1265            // more branches + recursion instead of a loop. I think the only1266            // thing that causes it to be a loop is the RootSuspendedAtTheShell1267            // check. If that's true, then we don't need a loop/recursion1268            // at all.1269            continue;1270          } else {1271            // The root errored yet again. Proceed to commit the tree.1272            if (enableProfilerTimer && enableComponentPerformanceTrack) {1273              renderEndTime = now();1274            }1275          }1276        }1277      }1278      if (exitStatus === RootFatalErrored) {1279        if (enableProfilerTimer && enableComponentPerformanceTrack) {1280          setCurrentTrackFromLanes(lanes);1281          logErroredRenderPhase(1282            renderStartTime,1283            renderEndTime,1284            lanes,1285            workInProgressUpdateTask,1286          );1287          finalizeRender(lanes, renderEndTime);1288        }1289        prepareFreshStack(root, NoLanes);1290        // Since this is a fatal error, we're going to pretend we attempted1291        // the entire tree, to avoid scheduling a prerender.1292        const didAttemptEntireTree = true;1293        markRootSuspended(root, lanes, NoLane, didAttemptEntireTree);1294        break;1295      }12961297      // We now have a consistent tree. The next step is either to commit it,1298      // or, if something suspended, wait to commit it after a timeout.1299      finishConcurrentRender(1300        root,1301        exitStatus,1302        finishedWork,1303        lanes,1304        renderEndTime,1305      );1306    }1307    break;1308  } while (true);13091310  ensureRootIsScheduled(root);1311}13121313function recoverFromConcurrentError(1314  root: FiberRoot,1315  originallyAttemptedLanes: Lanes,1316  errorRetryLanes: Lanes,1317): RootExitStatus {1318  // If an error occurred during hydration, discard server response and fall1319  // back to client side render.13201321  // Before rendering again, save the errors from the previous attempt.1322  const errorsFromFirstAttempt = workInProgressRootConcurrentErrors;13231324  // $FlowFixMe[constant-condition]1325  const wasRootDehydrated = supportsHydration && isRootDehydrated(root);1326  // $FlowFixMe[constant-condition]1327  if (wasRootDehydrated) {1328    // The shell failed to hydrate. Set a flag to force a client rendering1329    // during the next attempt. To do this, we call prepareFreshStack now1330    // to create the root work-in-progress fiber. This is a bit weird in terms1331    // of factoring, because it relies on renderRootSync not calling1332    // prepareFreshStack again in the call below, which happens because the1333    // root and lanes haven't changed.1334    //1335    // TODO: I think what we should do is set ForceClientRender inside1336    // throwException, like we do for nested Suspense boundaries. The reason1337    // it's here instead is so we can switch to the synchronous work loop, too.1338    // Something to consider for a future refactor.1339    const rootWorkInProgress = prepareFreshStack(root, errorRetryLanes);1340    rootWorkInProgress.flags |= ForceClientRender;1341  }13421343  const exitStatus = renderRootSync(root, errorRetryLanes, false);1344  if (exitStatus !== RootErrored) {1345    // Successfully finished rendering on retry13461347    if (workInProgressRootDidAttachPingListener && !wasRootDehydrated) {1348      // During the synchronous render, we attached additional ping listeners.1349      // This is highly suggestive of an uncached promise (though it's not the1350      // only reason this would happen). If it was an uncached promise, then1351      // it may have masked a downstream error from ocurring without actually1352      // fixing it. Example:1353      //1354      //    use(Promise.resolve('uncached'))1355      //    throw new Error('Oops!')1356      //1357      // When this happens, there's a conflict between blocking potential1358      // concurrent data races and unwrapping uncached promise values. We1359      // have to choose one or the other. Because the data race recovery is1360      // a last ditch effort, we'll disable it.1361      root.errorRecoveryDisabledLanes = mergeLanes(1362        root.errorRecoveryDisabledLanes,1363        originallyAttemptedLanes,1364      );13651366      // Mark the current render as suspended and force it to restart. Once1367      // these lanes finish successfully, we'll re-enable the error recovery1368      // mechanism for subsequent updates.1369      workInProgressRootInterleavedUpdatedLanes |= originallyAttemptedLanes;1370      return RootSuspendedWithDelay;1371    }13721373    // The errors from the failed first attempt have been recovered. Add1374    // them to the collection of recoverable errors. We'll log them in the1375    // commit phase.1376    const errorsFromSecondAttempt = workInProgressRootRecoverableErrors;1377    workInProgressRootRecoverableErrors = errorsFromFirstAttempt;1378    // The errors from the second attempt should be queued after the errors1379    // from the first attempt, to preserve the causal sequence.1380    if (errorsFromSecondAttempt !== null) {1381      queueRecoverableErrors(errorsFromSecondAttempt);1382    }1383  } else {1384    // The UI failed to recover.1385  }1386  return exitStatus;1387}13881389export function queueRecoverableErrors(errors: Array<CapturedValue<mixed>>) {1390  if (workInProgressRootRecoverableErrors === null) {1391    workInProgressRootRecoverableErrors = errors;1392  } else {1393    // $FlowFixMe[method-unbinding]1394    workInProgressRootRecoverableErrors.push.apply(1395      workInProgressRootRecoverableErrors,1396      errors,1397    );1398  }1399}14001401function finishConcurrentRender(1402  root: FiberRoot,1403  exitStatus: RootExitStatus,1404  finishedWork: Fiber,1405  lanes: Lanes,1406  renderEndTime: number, // Profiling-only1407) {1408  // TODO: The fact that most of these branches are identical suggests that some1409  // of the exit statuses are not best modeled as exit statuses and should be1410  // tracked orthogonally.1411  switch (exitStatus) {1412    case RootInProgress:1413    case RootFatalErrored: {1414      throw new Error('Root did not complete. This is a bug in React.');1415    }1416    case RootSuspendedWithDelay: {1417      if (!includesOnlyTransitions(lanes) && !includesOnlyRetries(lanes)) {1418        // Commit the placeholder.1419        break;1420      }1421    }1422    // Fallthrough1423    case RootSuspendedAtTheShell: {1424      // This is a transition, so we should exit without committing a1425      // placeholder and without scheduling a timeout. Delay indefinitely1426      // until we receive more data.1427      if (enableProfilerTimer && enableComponentPerformanceTrack) {1428        setCurrentTrackFromLanes(lanes);1429        logSuspendedRenderPhase(1430          renderStartTime,1431          renderEndTime,1432          lanes,1433          workInProgressUpdateTask,1434        );1435        finalizeRender(lanes, renderEndTime);1436        trackSuspendedTime(lanes, renderEndTime);1437      }1438      const didAttemptEntireTree = !workInProgressRootDidSkipSuspendedSiblings;1439      markRootSuspended(1440        root,1441        lanes,1442        workInProgressDeferredLane,1443        didAttemptEntireTree,1444      );1445      return;1446    }1447    case RootErrored: {1448      // This render errored. Ignore any recoverable errors because we weren't actually1449      // able to recover. Instead, whatever the final errors were is the ones we log.1450      // This ensures that we only log the actual client side error if it's just a plain1451      // error thrown from a component on the server and the client.1452      workInProgressRootRecoverableErrors = null;1453      break;1454    }1455    case RootSuspended:1456    case RootCompleted: {1457      break;1458    }1459    default: {1460      throw new Error('Unknown root exit status.');1461    }1462  }14631464  if (shouldForceFlushFallbacksInDEV()) {1465    // We're inside an `act` scope. Commit immediately.1466    completeRoot(1467      root,1468      finishedWork,1469      lanes,1470      workInProgressRootRecoverableErrors,1471      workInProgressTransitions,1472      workInProgressRootDidIncludeRecursiveRenderUpdate,1473      workInProgressDeferredLane,1474      workInProgressRootInterleavedUpdatedLanes,1475      workInProgressSuspendedRetryLanes,1476      workInProgressRootDidSkipSuspendedSiblings,1477      exitStatus,1478      null,1479      null,1480      renderStartTime,1481      renderEndTime,1482    );1483  } else {1484    if (1485      includesOnlyRetries(lanes) &&1486      (alwaysThrottleRetries || exitStatus === RootSuspended)1487    ) {1488      // This render only included retries, no updates. Throttle committing1489      // retries so that we don't show too many loading states too quickly.1490      const msUntilTimeout =1491        globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();14921493      // Don't bother with a very short suspense time.1494      if (msUntilTimeout > 10) {1495        const didAttemptEntireTree =1496          !workInProgressRootDidSkipSuspendedSiblings;1497        markRootSuspended(1498          root,1499          lanes,1500          workInProgressDeferredLane,1501          didAttemptEntireTree,1502        );15031504        const nextLanes = getNextLanes(root, NoLanes, true);1505        if (nextLanes !== NoLanes) {1506          // There's additional work we can do on this root. We might as well1507          // attempt to work on that while we're suspended.1508          return;1509        }15101511        // The render is suspended, it hasn't timed out, and there's no1512        // lower priority work to do. Instead of committing the fallback1513        // immediately, wait for more data to arrive.1514        // TODO: Combine retry throttling with Suspensey commits. Right now they1515        // run one after the other.1516        pendingEffectsLanes = lanes;1517        root.timeoutHandle = scheduleTimeout(1518          completeRootWhenReady.bind(1519            null,1520            root,1521            finishedWork,1522            workInProgressRootRecoverableErrors,1523            workInProgressTransitions,1524            workInProgressRootDidIncludeRecursiveRenderUpdate,1525            lanes,1526            workInProgressDeferredLane,1527            workInProgressRootInterleavedUpdatedLanes,1528            workInProgressSuspendedRetryLanes,1529            workInProgressRootDidSkipSuspendedSiblings,1530            exitStatus,1531            'Throttled',1532            renderStartTime,1533            renderEndTime,1534          ),1535          msUntilTimeout,1536        );1537        return;1538      }1539    }1540    completeRootWhenReady(1541      root,1542      finishedWork,1543      workInProgressRootRecoverableErrors,1544      workInProgressTransitions,1545      workInProgressRootDidIncludeRecursiveRenderUpdate,1546      lanes,1547      workInProgressDeferredLane,1548      workInProgressRootInterleavedUpdatedLanes,1549      workInProgressSuspendedRetryLanes,1550      workInProgressRootDidSkipSuspendedSiblings,1551      exitStatus,1552      null,1553      renderStartTime,1554      renderEndTime,1555    );1556  }1557}15581559function completeRootWhenReady(1560  root: FiberRoot,1561  finishedWork: Fiber,1562  recoverableErrors: Array<CapturedValue<mixed>> | null,1563  transitions: Array<Transition> | null,1564  didIncludeRenderPhaseUpdate: boolean,1565  lanes: Lanes,1566  spawnedLane: Lane,1567  updatedLanes: Lanes,1568  suspendedRetryLanes: Lanes,1569  didSkipSuspendedSiblings: boolean,1570  exitStatus: RootExitStatus,1571  suspendedCommitReason: SuspendedCommitReason, // Profiling-only1572  completedRenderStartTime: number, // Profiling-only1573  completedRenderEndTime: number, // Profiling-only1574) {1575  root.timeoutHandle = noTimeout;15761577  // TODO: Combine retry throttling with Suspensey commits. Right now they run1578  // one after the other.1579  const BothVisibilityAndMaySuspendCommit = Visibility | MaySuspendCommit;1580  const subtreeFlags = finishedWork.subtreeFlags;1581  const isViewTransitionEligible =1582    enableViewTransition && includesOnlyViewTransitionEligibleLanes(lanes); // TODO: Use a subtreeFlag to optimize.1583  const isGestureTransition = enableGestureTransition && isGestureRender(lanes);1584  const maySuspendCommit =1585    subtreeFlags & ShouldSuspendCommit ||1586    (subtreeFlags & BothVisibilityAndMaySuspendCommit) ===1587      BothVisibilityAndMaySuspendCommit;1588  let suspendedState: null | SuspendedState = null;1589  if (isViewTransitionEligible || maySuspendCommit || isGestureTransition) {1590    // Before committing, ask the renderer whether the host tree is ready.1591    // If it's not, we'll wait until it notifies us.1592    suspendedState = startSuspendingCommit();1593    // This will walk the completed fiber tree and attach listeners to all1594    // the suspensey resources. The renderer is responsible for accumulating1595    // all the load events. This all happens in a single synchronous1596    // transaction, so it track state in its own module scope.1597    // This will also track any newly added or appearing ViewTransition1598    // components for the purposes of forming pairs.1599    accumulateSuspenseyCommit(finishedWork, lanes, suspendedState);1600    if (1601      isViewTransitionEligible ||1602      (isGestureTransition &&1603        root.pendingGestures !== null &&1604        // If this gesture already has a View Transition running then we don't1605        // have to wait on that one before proceeding. We may hold the commit1606        // on the gesture committing later on in completeRoot.1607        root.pendingGestures.running === null)1608    ) {1609      // Wait for any pending View Transition (including gestures) to finish.1610      suspendOnActiveViewTransition(suspendedState, root.containerInfo);1611    }1612    // For timeouts we use the previous fallback commit for retries and1613    // the start time of the transition for transitions. This offset1614    // represents the time already passed.1615    const timeoutOffset = includesOnlyRetries(lanes)1616      ? globalMostRecentFallbackTime - now()1617      : includesOnlyTransitions(lanes)1618        ? globalMostRecentTransitionTime - now()1619        : 0;1620    // At the end, ask the renderer if it's ready to commit, or if we should1621    // suspend. If it's not ready, it will return a callback to subscribe to1622    // a ready event.1623    const schedulePendingCommit = waitForCommitToBeReady(1624      suspendedState,1625      timeoutOffset,1626    );1627    if (schedulePendingCommit !== null) {1628      // NOTE: waitForCommitToBeReady returns a subscribe function so that we1629      // only allocate a function if the commit isn't ready yet. The other1630      // pattern would be to always pass a callback to waitForCommitToBeReady.16311632      // Not yet ready to commit. Delay the commit until the renderer notifies1633      // us that it's ready. This will be canceled if we start work on the1634      // root again.1635      pendingEffectsLanes = lanes;1636      root.cancelPendingCommit = schedulePendingCommit(1637        completeRoot.bind(1638          null,1639          root,1640          finishedWork,1641          lanes,1642          recoverableErrors,1643          transitions,1644          didIncludeRenderPhaseUpdate,1645          spawnedLane,1646          updatedLanes,1647          suspendedRetryLanes,1648          didSkipSuspendedSiblings,1649          exitStatus,1650          suspendedState,1651          enableProfilerTimer1652            ? getSuspendedCommitReason(suspendedState, root.containerInfo)1653            : null,1654          completedRenderStartTime,1655          completedRenderEndTime,1656        ),1657      );1658      const didAttemptEntireTree = !didSkipSuspendedSiblings;1659      markRootSuspended(root, lanes, spawnedLane, didAttemptEntireTree);1660      return;1661    }1662  }16631664  // Otherwise, commit immediately.;1665  completeRoot(1666    root,1667    finishedWork,1668    lanes,1669    recoverableErrors,1670    transitions,1671    didIncludeRenderPhaseUpdate,1672    spawnedLane,1673    updatedLanes,1674    suspendedRetryLanes,1675    didSkipSuspendedSiblings,1676    exitStatus,1677    suspendedState,1678    suspendedCommitReason,1679    completedRenderStartTime,1680    completedRenderEndTime,1681  );1682}16831684function isRenderConsistentWithExternalStores(finishedWork: Fiber): boolean {1685  // Search the rendered tree for external store reads, and check whether the1686  // stores were mutated in a concurrent event. Intentionally using an iterative1687  // loop instead of recursion so we can exit early.1688  let node: Fiber = finishedWork;1689  while (true) {1690    const tag = node.tag;1691    if (1692      (tag === FunctionComponent ||1693        tag === ForwardRef ||1694        tag === SimpleMemoComponent) &&1695      node.flags & StoreConsistency1696    ) {1697      const updateQueue: FunctionComponentUpdateQueue | null =1698        node.updateQueue as any;1699      if (updateQueue !== null) {1700        const checks = updateQueue.stores;1701        if (checks !== null) {1702          for (let i = 0; i < checks.length; i++) {1703            const check = checks[i];1704            const getSnapshot = check.getSnapshot;1705            const renderedValue = check.value;1706            try {1707              if (!is(getSnapshot(), renderedValue)) {1708                // Found an inconsistent store.1709                return false;1710              }1711            } catch (error) {1712              // If `getSnapshot` throws, return `false`. This will schedule1713              // a re-render, and the error will be rethrown during render.1714              return false;1715            }1716          }1717        }1718      }1719    }1720    const child = node.child;1721    if (node.subtreeFlags & StoreConsistency && child !== null) {1722      child.return = node;1723      node = child;1724      continue;1725    }1726    if (node === finishedWork) {1727      return true;1728    }1729    while (node.sibling === null) {1730      if (node.return === null || node.return === finishedWork) {1731        return true;1732      }1733      node = node.return;1734    }1735    node.sibling.return = node.return;1736    node = node.sibling;1737  }1738  // Flow doesn't know this is unreachable, but eslint does1739  // eslint-disable-next-line no-unreachable1740  return true;1741}17421743// The extra indirections around markRootUpdated and markRootSuspended is1744// needed to avoid a circular dependency between this module and1745// ReactFiberLane. There's probably a better way to split up these modules and1746// avoid this problem. Perhaps all the root-marking functions should move into1747// the work loop.17481749function markRootUpdated(root: FiberRoot, updatedLanes: Lanes) {1750  _markRootUpdated(root, updatedLanes);17511752  if (enableInfiniteRenderLoopDetection) {1753    // Check for recursive updates1754    if (executionContext & RenderContext) {1755      workInProgressRootDidIncludeRecursiveRenderUpdate = true;1756    } else if (executionContext & CommitContext) {1757      didIncludeCommitPhaseUpdate = true;1758    }17591760    throwIfInfiniteUpdateLoopDetected(true);1761  }1762}17631764function markRootPinged(root: FiberRoot, pingedLanes: Lanes) {1765  _markRootPinged(root, pingedLanes);17661767  if (enableInfiniteRenderLoopDetection) {1768    // Check for recursive pings. Pings are conceptually different from updates in1769    // other contexts but we call it an "update" in this context because1770    // repeatedly pinging a suspended render can cause a recursive render loop.1771    // The relevant property is that it can result in a new render attempt1772    // being scheduled.1773    if (executionContext & RenderContext) {1774      workInProgressRootDidIncludeRecursiveRenderUpdate = true;1775    } else if (executionContext & CommitContext) {1776      didIncludeCommitPhaseUpdate = true;1777    }17781779    throwIfInfiniteUpdateLoopDetected(true);1780  }1781}17821783function markRootSuspended(1784  root: FiberRoot,1785  suspendedLanes: Lanes,1786  spawnedLane: Lane,1787  didAttemptEntireTree: boolean,1788) {1789  if (enableParallelTransitions) {1790    // When suspending, we should always mark the entangled lanes as suspended.1791    suspendedLanes = getEntangledLanes(root, suspendedLanes);1792  }17931794  // When suspending, we should always exclude lanes that were pinged or (more1795  // rarely, since we try to avoid it) updated during the render phase.1796  suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);1797  suspendedLanes = removeLanes(1798    suspendedLanes,1799    workInProgressRootInterleavedUpdatedLanes,1800  );1801  _markRootSuspended(root, suspendedLanes, spawnedLane, didAttemptEntireTree);1802}18031804export function flushRoot(root: FiberRoot, lanes: Lanes) {1805  if (lanes !== NoLanes) {1806    upgradePendingLanesToSync(root, lanes);1807    ensureRootIsScheduled(root);1808    if ((executionContext & (RenderContext | CommitContext)) === NoContext) {1809      resetRenderTimer();1810      // TODO: For historical reasons this flushes all sync work across all1811      // roots. It shouldn't really matter either way, but we could change this1812      // to only flush the given root.1813      flushSyncWorkOnAllRoots();1814    }1815  }1816}18171818export function getExecutionContext(): ExecutionContext {1819  return executionContext;1820}18211822export function deferredUpdates<A>(fn: () => A): A {1823  const prevTransition = ReactSharedInternals.T;1824  const previousPriority = getCurrentUpdatePriority();1825  try {1826    setCurrentUpdatePriority(DefaultEventPriority);1827    ReactSharedInternals.T = null;1828    return fn();1829  } finally {1830    setCurrentUpdatePriority(previousPriority);1831    ReactSharedInternals.T = prevTransition;1832  }1833}18341835export function batchedUpdates<A, R>(fn: A => R, a: A): R {1836  if (disableLegacyMode) {1837    // batchedUpdates is a no-op now, but there's still some internal react-dom1838    // code calling it, that we can't remove until we remove legacy mode.1839    return fn(a);1840  } else {1841    const prevExecutionContext = executionContext;1842    executionContext |= BatchedContext;1843    try {1844      return fn(a);1845    } finally {1846      executionContext = prevExecutionContext;1847      // If there were legacy sync updates, flush them at the end of the outer1848      // most batchedUpdates-like method.1849      if (1850        executionContext === NoContext &&1851        // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.1852        !(__DEV__ && ReactSharedInternals.isBatchingLegacy)1853      ) {1854        resetRenderTimer();1855        flushSyncWorkOnLegacyRootsOnly();1856      }1857    }1858  }1859}18601861export function discreteUpdates<A, B, C, D, R>(1862  fn: (A, B, C, D) => R,1863  a: A,1864  b: B,1865  c: C,1866  d: D,1867): R {1868  const prevTransition = ReactSharedInternals.T;1869  const previousPriority = getCurrentUpdatePriority();1870  try {1871    setCurrentUpdatePriority(DiscreteEventPriority);1872    ReactSharedInternals.T = null;1873    return fn(a, b, c, d);1874  } finally {1875    setCurrentUpdatePriority(previousPriority);1876    ReactSharedInternals.T = prevTransition;1877    if (executionContext === NoContext) {1878      resetRenderTimer();1879    }1880  }1881}18821883// Overload the definition to the two valid signatures.1884// Warning, this opts-out of checking the function body.1885declare function flushSyncFromReconciler<R>(fn: () => R): R;1886declare function flushSyncFromReconciler(void): void;1887export function flushSyncFromReconciler<R>(fn: (() => R) | void): R | void {1888  // In legacy mode, we flush pending passive effects at the beginning of the1889  // next event, not at the end of the previous one.1890  if (1891    pendingEffectsStatus !== NO_PENDING_EFFECTS &&1892    !disableLegacyMode &&1893    pendingEffectsRoot.tag === LegacyRoot &&1894    (executionContext & (RenderContext | CommitContext)) === NoContext1895  ) {1896    flushPendingEffects();1897  }18981899  const prevExecutionContext = executionContext;1900  executionContext |= BatchedContext;19011902  const prevTransition = ReactSharedInternals.T;1903  const previousPriority = getCurrentUpdatePriority();19041905  try {1906    setCurrentUpdatePriority(DiscreteEventPriority);1907    ReactSharedInternals.T = null;1908    if (fn) {1909      return fn();1910    } else {1911      return undefined;1912    }1913  } finally {1914    setCurrentUpdatePriority(previousPriority);1915    ReactSharedInternals.T = prevTransition;19161917    executionContext = prevExecutionContext;1918    // Flush the immediate callbacks that were scheduled during this batch.1919    // Note that this will happen even if batchedUpdates is higher up1920    // the stack.1921    if ((executionContext & (RenderContext | CommitContext)) === NoContext) {1922      flushSyncWorkOnAllRoots();1923    }1924  }1925}19261927// If called outside of a render or commit will flush all sync work on all roots1928// Returns whether the the call was during a render or not1929export function flushSyncWork(): boolean {1930  if ((executionContext & (RenderContext | CommitContext)) === NoContext) {1931    flushSyncWorkOnAllRoots();1932    return false;1933  }1934  return true;1935}19361937export function isAlreadyRendering(): boolean {1938  // Used by the renderer to print a warning if certain APIs are called from1939  // the wrong context, and for profiling warnings.1940  return (executionContext & (RenderContext | CommitContext)) !== NoContext;1941}19421943export function isInvalidExecutionContextForEventFunction(): boolean {1944  // Used to throw if certain APIs are called from the wrong context.1945  return (executionContext & RenderContext) !== NoContext;1946}19471948// This is called by the HiddenContext module when we enter or leave a1949// hidden subtree. The stack logic is managed there because that's the only1950// place that ever modifies it. Which module it lives in doesn't matter for1951// performance because this function will get inlined regardless1952export function setEntangledRenderLanes(newEntangledRenderLanes: Lanes) {1953  entangledRenderLanes = newEntangledRenderLanes;1954}19551956export function getEntangledRenderLanes(): Lanes {1957  return entangledRenderLanes;1958}19591960function resetWorkInProgressStack() {1961  if (workInProgress === null) return;1962  let interruptedWork;1963  if (workInProgressSuspendedReason === NotSuspended) {1964    // Normal case. Work-in-progress hasn't started yet. Unwind all1965    // its parents.1966    interruptedWork = workInProgress.return;1967  } else {1968    // Work-in-progress is in suspended state. Reset the work loop and unwind1969    // both the suspended fiber and all its parents.1970    resetSuspendedWorkLoopOnUnwind(workInProgress);1971    interruptedWork = workInProgress;1972  }1973  while (interruptedWork !== null) {1974    const current = interruptedWork.alternate;1975    unwindInterruptedWork(1976      current,1977      interruptedWork,1978      workInProgressRootRenderLanes,1979    );1980    interruptedWork = interruptedWork.return;1981  }1982  workInProgress = null;1983}19841985function finalizeRender(lanes: Lanes, finalizationTime: number): void {1986  if (enableProfilerTimer && enableComponentPerformanceTrack) {1987    if (isGestureRender(lanes)) {1988      clampGestureTimers(finalizationTime);1989    } else if (includesBlockingLane(lanes)) {1990      clampBlockingTimers(finalizationTime);1991    }1992    if (includesTransitionLane(lanes)) {1993      clampTransitionTimers(finalizationTime);1994    }1995    if (includesRetryLane(lanes)) {1996      clampRetryTimers(finalizationTime);1997    }1998    if (includesIdleGroupLanes(lanes)) {1999      clampIdleTimers(finalizationTime);2000    }

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.