packages/react-reconciler/src/ReactFiberWorkLoop.js JAVASCRIPT 5,692 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 5,692.
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} from 'shared/ReactFeatureFlags';63import {resetOwnerStackLimit} from 'shared/ReactOwnerStackReset';64import ReactSharedInternals from 'shared/ReactSharedInternals';65import is from 'shared/objectIs';6667import reportGlobalError from 'shared/reportGlobalError';6869import {70  // Aliased because `act` will override and push to an internal queue71  scheduleCallback as Scheduler_scheduleCallback,72  shouldYield,73  requestPaint,74  now,75  NormalPriority as NormalSchedulerPriority,76  IdlePriority as IdleSchedulerPriority,77} from './Scheduler';78import {79  logBlockingStart,80  logGestureStart,81  logTransitionStart,82  logRenderPhase,83  logInterruptedRenderPhase,84  logSuspendedRenderPhase,85  logRecoveredRenderPhase,86  logErroredRenderPhase,87  logInconsistentRender,88  logSuspendedWithDelayPhase,89  logSuspendedCommitPhase,90  logSuspendedViewTransitionPhase,91  logCommitPhase,92  logPaintYieldPhase,93  logStartViewTransitionYieldPhase,94  logAnimatingPhase,95  logPassiveCommitPhase,96  logYieldTime,97  logActionYieldTime,98  logSuspendedYieldTime,99  setCurrentTrackFromLanes,100  markAllLanesInOrder,101  logApplyGesturePhase,102} from './ReactFiberPerformanceTrack';103104import {105  resetAfterCommit,106  scheduleTimeout,107  cancelTimeout,108  noTimeout,109  afterActiveInstanceBlur,110  startSuspendingCommit,111  suspendOnActiveViewTransition,112  waitForCommitToBeReady,113  getSuspendedCommitReason,114  preloadInstance,115  preloadResource,116  supportsHydration,117  setCurrentUpdatePriority,118  getCurrentUpdatePriority,119  resolveUpdatePriority,120  trackSchedulerEvent,121  startViewTransition,122  startGestureTransition,123  stopViewTransition,124  addViewTransitionFinishedListener,125  createViewTransitionInstance,126  flushHydrationEvents,127} from './ReactFiberConfig';128129import {createWorkInProgress, resetWorkInProgress} from './ReactFiber';130import {isRootDehydrated} from './ReactFiberShellHydration';131import {132  getIsHydrating,133  popHydrationStateOnInterruptedWork,134} from './ReactFiberHydrationContext';135import {136  NoMode,137  ProfileMode,138  ConcurrentMode,139  StrictLegacyMode,140  StrictEffectsMode,141} from './ReactTypeOfMode';142import {143  HostRoot,144  ClassComponent,145  ActivityComponent,146  SuspenseComponent,147  SuspenseListComponent,148  OffscreenComponent,149  FunctionComponent,150  ForwardRef,151  MemoComponent,152  SimpleMemoComponent,153  HostComponent,154  HostHoistable,155  HostSingleton,156} from './ReactWorkTags';157import {ConcurrentRoot, LegacyRoot} from './ReactRootTags';158import type {Flags} from './ReactFiberFlags';159import {160  NoFlags,161  Incomplete,162  StoreConsistency,163  HostEffectMask,164  ForceClientRender,165  BeforeMutationMask,166  MutationMask,167  LayoutMask,168  PassiveMask,169  PlacementDEV,170  Visibility,171  MountPassiveDev,172  MountLayoutDev,173  DidDefer,174  ShouldSuspendCommit,175  MaySuspendCommit,176  ScheduleRetry,177  PassiveTransitionMask,178} from './ReactFiberFlags';179import {180  NoLanes,181  NoLane,182  SyncLane,183  claimNextRetryLane,184  includesSyncLane,185  isSubsetOfLanes,186  mergeLanes,187  removeLanes,188  pickArbitraryLane,189  includesNonIdleWork,190  includesOnlyRetries,191  includesOnlyTransitions,192  includesBlockingLane,193  includesTransitionLane,194  includesRetryLane,195  includesIdleGroupLanes,196  includesExpiredLane,197  getNextLanes,198  getEntangledLanes,199  getLanesToRetrySynchronouslyOnError,200  upgradePendingLanesToSync,201  markRootSuspended as _markRootSuspended,202  markRootUpdated as _markRootUpdated,203  markRootPinged as _markRootPinged,204  markRootFinished,205  addFiberToLanesMap,206  movePendingFibersToMemoized,207  addTransitionToLanesMap,208  getTransitionsForLanes,209  includesSomeLane,210  OffscreenLane,211  SyncUpdateLanes,212  UpdateLanes,213  claimNextTransitionDeferredLane,214  checkIfRootIsPrerendering,215  includesOnlyViewTransitionEligibleLanes,216  isGestureRender,217  GestureLane,218  SomeTransitionLane,219  SomeRetryLane,220  IdleLane,221} from './ReactFiberLane';222import {223  DiscreteEventPriority,224  DefaultEventPriority,225  lowerEventPriority,226  lanesToEventPriority,227  eventPriorityToLane,228} from './ReactEventPriorities';229import {requestCurrentTransition} from './ReactFiberTransition';230import {231  SelectiveHydrationException,232  beginWork,233  replayFunctionComponent,234} from './ReactFiberBeginWork';235import {completeWork} from './ReactFiberCompleteWork';236import {unwindWork, unwindInterruptedWork} from './ReactFiberUnwindWork';237import {238  throwException,239  createRootErrorUpdate,240  createClassErrorUpdate,241  initializeClassErrorUpdate,242} from './ReactFiberThrow';243import {244  commitBeforeMutationEffects,245  shouldFireAfterActiveInstanceBlur,246  commitAfterMutationEffects,247  commitLayoutEffects,248  commitMutationEffects,249  commitPassiveMountEffects,250  commitPassiveUnmountEffects,251  disappearLayoutEffectsForDEVValidation,252  reconnectPassiveEffects,253  reappearLayoutEffectsForDEVValidation,254  disconnectPassiveEffect,255  invokeLayoutEffectMountInDEV,256  invokePassiveEffectMountInDEV,257  invokeLayoutEffectUnmountInDEV,258  invokePassiveEffectUnmountInDEV,259  accumulateSuspenseyCommit,260} from './ReactFiberCommitWork';261import {resetShouldStartViewTransition} from './ReactFiberCommitViewTransitions';262import {shouldStartViewTransition} from './ReactFiberCommitViewTransitions';263import {264  insertDestinationClones,265  applyDepartureTransitions,266  startGestureAnimations,267} from './ReactFiberApplyGesture';268import {enqueueUpdate} from './ReactFiberClassUpdateQueue';269import {resetContextDependencies} from './ReactFiberNewContext';270import {271  resetHooksAfterThrow,272  resetHooksOnUnwind,273  ContextOnlyDispatcher,274} from './ReactFiberHooks';275import {DefaultAsyncDispatcher} from './ReactFiberAsyncDispatcher';276import {277  createCapturedValueAtFiber,278  type CapturedValue,279} from './ReactCapturedValue';280import {281  enqueueConcurrentRenderForLane,282  finishQueueingConcurrentUpdates,283  getConcurrentlyUpdatedLanes,284} from './ReactFiberConcurrentUpdates';285286import {287  blockingClampTime,288  blockingUpdateTime,289  blockingUpdateTask,290  blockingUpdateType,291  blockingUpdateMethodName,292  blockingUpdateComponentName,293  blockingEventTime,294  blockingEventType,295  blockingEventRepeatTime,296  blockingSuspendedTime,297  gestureClampTime,298  gestureUpdateTime,299  gestureUpdateTask,300  gestureUpdateType,301  gestureUpdateMethodName,302  gestureUpdateComponentName,303  gestureEventTime,304  gestureEventType,305  gestureEventRepeatTime,306  gestureSuspendedTime,307  transitionClampTime,308  transitionStartTime,309  transitionUpdateTime,310  transitionUpdateTask,311  transitionUpdateType,312  transitionUpdateMethodName,313  transitionUpdateComponentName,314  transitionEventTime,315  transitionEventType,316  transitionEventRepeatTime,317  transitionSuspendedTime,318  clearBlockingTimers,319  clearGestureTimers,320  clearGestureUpdates,321  clearTransitionTimers,322  clampBlockingTimers,323  clampGestureTimers,324  clampTransitionTimers,325  clampRetryTimers,326  clampIdleTimers,327  markNestedUpdateScheduled,328  renderStartTime,329  commitStartTime,330  commitEndTime,331  commitErrors,332  recordRenderTime,333  recordCommitTime,334  recordCommitEndTime,335  startProfilerTimer,336  stopProfilerTimerIfRunningAndRecordDuration,337  stopProfilerTimerIfRunningAndRecordIncompleteDuration,338  trackSuspendedTime,339  startYieldTimer,340  yieldStartTime,341  yieldReason,342  startPingTimerByLanes,343  recordEffectError,344  resetCommitErrors,345  PINGED_UPDATE,346  SPAWNED_UPDATE,347  startAnimating,348  stopAnimating,349  animatingLanes,350  retryClampTime,351  idleClampTime,352  animatingTask,353} from './ReactProfilerTimer';354355// DEV stuff356import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';357import ReactStrictModeWarnings from './ReactStrictModeWarnings';358import {359  isRendering as ReactCurrentDebugFiberIsRenderingInDEV,360  resetCurrentFiber,361  runWithFiberInDEV,362} from './ReactCurrentFiber';363import {364  isDevToolsPresent,365  markCommitStarted,366  markCommitStopped,367  markComponentRenderStopped,368  markComponentSuspended,369  markComponentErrored,370  markLayoutEffectsStarted,371  markLayoutEffectsStopped,372  markPassiveEffectsStarted,373  markPassiveEffectsStopped,374  markRenderStarted,375  markRenderYielded,376  markRenderStopped,377  onCommitRoot as onCommitRootDevTools,378  onPostCommitRoot as onPostCommitRootDevTools,379  setIsStrictModeForDevtools,380} from './ReactFiberDevToolsHook';381import {onCommitRoot as onCommitRootTestSelector} from './ReactTestSelectors';382import {releaseCache} from './ReactFiberCacheComponent';383import {384  isLegacyActEnvironment,385  isConcurrentActEnvironment,386} from './ReactFiberAct';387import {processTransitionCallbacks} from './ReactFiberTracingMarkerComponent';388import {389  SuspenseException,390  SuspenseActionException,391  SuspenseyCommitException,392  getSuspendedThenable,393  isThenableResolved,394  hasPotentialUseWarnings,395  clearUseWarnings,396} from './ReactFiberThenable';397import {schedulePostPaintCallback} from './ReactPostPaintCallback';398import {399  getSuspenseHandler,400  getShellBoundary,401} from './ReactFiberSuspenseContext';402import {resetChildReconcilerOnUnwind} from './ReactChildFiber';403import {404  ensureRootIsScheduled,405  flushSyncWorkOnAllRoots,406  flushSyncWorkOnLegacyRootsOnly,407  requestTransitionLane,408} from './ReactFiberRootScheduler';409import {getMaskedContext, getUnmaskedContext} from './ReactFiberLegacyContext';410import {logUncaughtError} from './ReactFiberErrorLogger';411import {412  scheduleGestureCommit,413  stopCommittedGesture,414} from './ReactFiberGestureScheduler';415import {claimQueuedTransitionTypes} from './ReactFiberTransitionTypes';416417const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;418419type ExecutionContext = number;420421export const NoContext = /*             */ 0b000;422const BatchedContext = /*               */ 0b001;423export const RenderContext = /*         */ 0b010;424export const CommitContext = /*         */ 0b100;425426type RootExitStatus = 0 | 1 | 2 | 3 | 4 | 5 | 6;427const RootInProgress = 0;428const RootFatalErrored = 1;429const RootErrored = 2;430const RootSuspended = 3;431const RootSuspendedWithDelay = 4;432const RootSuspendedAtTheShell = 6;433const RootCompleted = 5;434435// Describes where we are in the React execution stack436let executionContext: ExecutionContext = NoContext;437// The root we're working on438let workInProgressRoot: FiberRoot | null = null;439// The fiber we're working on440let workInProgress: Fiber | null = null;441// The lanes we're rendering442let workInProgressRootRenderLanes: Lanes = NoLanes;443444export opaque type SuspendedReason = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;445const NotSuspended: SuspendedReason = 0;446const SuspendedOnError: SuspendedReason = 1;447const SuspendedOnData: SuspendedReason = 2;448const SuspendedOnImmediate: SuspendedReason = 3;449const SuspendedOnInstance: SuspendedReason = 4;450const SuspendedOnInstanceAndReadyToContinue: SuspendedReason = 5;451const SuspendedOnDeprecatedThrowPromise: SuspendedReason = 6;452const SuspendedAndReadyToContinue: SuspendedReason = 7;453const SuspendedOnHydration: SuspendedReason = 8;454const SuspendedOnAction: SuspendedReason = 9;455456// When this is true, the work-in-progress fiber just suspended (or errored) and457// we've yet to unwind the stack. In some cases, we may yield to the main thread458// after this happens. If the fiber is pinged before we resume, we can retry459// immediately instead of unwinding the stack.460let workInProgressSuspendedReason: SuspendedReason = NotSuspended;461let workInProgressThrownValue: mixed = null;462463// Tracks whether any siblings were skipped during the unwind phase after464// something suspends. Used to determine whether to schedule another render465// to prewarm the skipped siblings.466let workInProgressRootDidSkipSuspendedSiblings: boolean = false;467// Whether the work-in-progress render is the result of a prewarm/prerender.468// This tells us whether or not we should render the siblings after469// something suspends.470let workInProgressRootIsPrerendering: boolean = false;471472// Whether a ping listener was attached during this render. This is slightly473// different that whether something suspended, because we don't add multiple474// listeners to a promise we've already seen (per root and lane).475let workInProgressRootDidAttachPingListener: boolean = false;476477// A contextual version of workInProgressRootRenderLanes. It is a superset of478// the lanes that we started working on at the root. When we enter a subtree479// that is currently hidden, we add the lanes that would have committed if480// the hidden tree hadn't been deferred. This is modified by the481// HiddenContext module.482//483// Most things in the work loop should deal with workInProgressRootRenderLanes.484// Most things in begin/complete phases should deal with entangledRenderLanes.485export let entangledRenderLanes: Lanes = NoLanes;486487// Whether to root completed, errored, suspended, etc.488let workInProgressRootExitStatus: RootExitStatus = RootInProgress;489// The work left over by components that were visited during this render. Only490// includes unprocessed updates, not work in bailed out children.491let workInProgressRootSkippedLanes: Lanes = NoLanes;492// Lanes that were updated (in an interleaved event) during this render.493let workInProgressRootInterleavedUpdatedLanes: Lanes = NoLanes;494// Lanes that were updated during the render phase (*not* an interleaved event).495let workInProgressRootRenderPhaseUpdatedLanes: Lanes = NoLanes;496// Lanes that were pinged (in an interleaved event) during this render.497let workInProgressRootPingedLanes: Lanes = NoLanes;498// If this render scheduled deferred work, this is the lane of the deferred task.499let workInProgressDeferredLane: Lane = NoLane;500// Represents the retry lanes that were spawned by this render and have not501// been pinged since, implying that they are still suspended.502let workInProgressSuspendedRetryLanes: Lanes = NoLanes;503// Errors that are thrown during the render phase.504let workInProgressRootConcurrentErrors: Array<CapturedValue<mixed>> | null =505  null;506// These are errors that we recovered from without surfacing them to the UI.507// We will log them once the tree commits.508let workInProgressRootRecoverableErrors: Array<CapturedValue<mixed>> | null =509  null;510511// Tracks when an update occurs during the render phase.512let workInProgressRootDidIncludeRecursiveRenderUpdate: boolean = false;513// Thacks when an update occurs during the commit phase. It's a separate514// variable from the one for renders because the commit phase may run515// concurrently to a render phase.516let didIncludeCommitPhaseUpdate: boolean = false;517// The most recent time we either committed a fallback, or when a fallback was518// filled in with the resolved UI. This lets us throttle the appearance of new519// content as it streams in, to minimize jank.520// TODO: Think of a better name for this variable?521let globalMostRecentFallbackTime: number = 0;522// Track the most recent time we started a new Transition. This lets us apply523// heuristics like the suspensey image timeout based on how long we've waited524// already.525let globalMostRecentTransitionTime: number = 0;526527const FALLBACK_THROTTLE_MS: number = 300;528529// The absolute time for when we should start giving up on rendering530// more and prefer CPU suspense heuristics instead.531let workInProgressRootRenderTargetTime: number = Infinity;532// How long a render is supposed to take before we start following CPU533// suspense heuristics and opt out of rendering more content.534const RENDER_TIMEOUT_MS = 500;535536let workInProgressTransitions: Array<Transition> | null = null;537export function getWorkInProgressTransitions(): null | Array<Transition> {538  return workInProgressTransitions;539}540541// The first setState call that eventually caused the current render.542let workInProgressUpdateTask: null | ConsoleTask = null;543544let currentPendingTransitionCallbacks: PendingTransitionCallbacks | null = null;545let currentEndTime: number | null = null;546547export function addTransitionStartCallbackToPendingTransition(548  transition: Transition,549) {550  if (enableTransitionTracing) {551    if (currentPendingTransitionCallbacks === null) {552      currentPendingTransitionCallbacks = {553        transitionStart: [],554        transitionProgress: null,555        transitionComplete: null,556        markerProgress: null,557        markerIncomplete: null,558        markerComplete: null,559      };560    }561562    if (currentPendingTransitionCallbacks.transitionStart === null) {563      currentPendingTransitionCallbacks.transitionStart =564        [] as Array<Transition>;565    }566567    currentPendingTransitionCallbacks.transitionStart.push(transition);568  }569}570571export function addMarkerProgressCallbackToPendingTransition(572  markerName: string,573  transitions: Set<Transition>,574  pendingBoundaries: PendingBoundaries,575) {576  if (enableTransitionTracing) {577    if (currentPendingTransitionCallbacks === null) {578      currentPendingTransitionCallbacks = {579        transitionStart: null,580        transitionProgress: null,581        transitionComplete: null,582        markerProgress: new Map(),583        markerIncomplete: null,584        markerComplete: null,585      } as PendingTransitionCallbacks;586    }587588    if (currentPendingTransitionCallbacks.markerProgress === null) {589      currentPendingTransitionCallbacks.markerProgress = new Map();590    }591592    currentPendingTransitionCallbacks.markerProgress.set(markerName, {593      pendingBoundaries,594      transitions,595    });596  }597}598599export function addMarkerIncompleteCallbackToPendingTransition(600  markerName: string,601  transitions: Set<Transition>,602  aborts: Array<TransitionAbort>,603) {604  if (enableTransitionTracing) {605    if (currentPendingTransitionCallbacks === null) {606      currentPendingTransitionCallbacks = {607        transitionStart: null,608        transitionProgress: null,609        transitionComplete: null,610        markerProgress: null,611        markerIncomplete: new Map(),612        markerComplete: null,613      };614    }615616    if (currentPendingTransitionCallbacks.markerIncomplete === null) {617      currentPendingTransitionCallbacks.markerIncomplete = new Map();618    }619620    currentPendingTransitionCallbacks.markerIncomplete.set(markerName, {621      transitions,622      aborts,623    });624  }625}626627export function addMarkerCompleteCallbackToPendingTransition(628  markerName: string,629  transitions: Set<Transition>,630) {631  if (enableTransitionTracing) {632    if (currentPendingTransitionCallbacks === null) {633      currentPendingTransitionCallbacks = {634        transitionStart: null,635        transitionProgress: null,636        transitionComplete: null,637        markerProgress: null,638        markerIncomplete: null,639        markerComplete: new Map(),640      };641    }642643    if (currentPendingTransitionCallbacks.markerComplete === null) {644      currentPendingTransitionCallbacks.markerComplete = new Map();645    }646647    currentPendingTransitionCallbacks.markerComplete.set(648      markerName,649      transitions,650    );651  }652}653654export function addTransitionProgressCallbackToPendingTransition(655  transition: Transition,656  boundaries: PendingBoundaries,657) {658  if (enableTransitionTracing) {659    if (currentPendingTransitionCallbacks === null) {660      currentPendingTransitionCallbacks = {661        transitionStart: null,662        transitionProgress: new Map(),663        transitionComplete: null,664        markerProgress: null,665        markerIncomplete: null,666        markerComplete: null,667      };668    }669670    if (currentPendingTransitionCallbacks.transitionProgress === null) {671      currentPendingTransitionCallbacks.transitionProgress = new Map();672    }673674    currentPendingTransitionCallbacks.transitionProgress.set(675      transition,676      boundaries,677    );678  }679}680681export function addTransitionCompleteCallbackToPendingTransition(682  transition: Transition,683) {684  if (enableTransitionTracing) {685    if (currentPendingTransitionCallbacks === null) {686      currentPendingTransitionCallbacks = {687        transitionStart: null,688        transitionProgress: null,689        transitionComplete: [],690        markerProgress: null,691        markerIncomplete: null,692        markerComplete: null,693      };694    }695696    if (currentPendingTransitionCallbacks.transitionComplete === null) {697      currentPendingTransitionCallbacks.transitionComplete =698        [] as Array<Transition>;699    }700701    currentPendingTransitionCallbacks.transitionComplete.push(transition);702  }703}704705function resetRenderTimer() {706  workInProgressRootRenderTargetTime = now() + RENDER_TIMEOUT_MS;707}708709export function getRenderTargetTime(): number {710  return workInProgressRootRenderTargetTime;711}712713let legacyErrorBoundariesThatAlreadyFailed: Set<mixed> | null = null;714715type SuspendedCommitReason = null | string;716717type DelayedCommitReason = 0 | 1 | 2 | 3;718const IMMEDIATE_COMMIT = 0;719const ABORTED_VIEW_TRANSITION_COMMIT = 1;720const DELAYED_PASSIVE_COMMIT = 2;721const ANIMATION_STARTED_COMMIT = 3;722723const NO_PENDING_EFFECTS = 0;724const PENDING_MUTATION_PHASE = 1;725const PENDING_LAYOUT_PHASE = 2;726const PENDING_AFTER_MUTATION_PHASE = 3;727const PENDING_SPAWNED_WORK = 4;728const PENDING_PASSIVE_PHASE = 5;729const PENDING_GESTURE_MUTATION_PHASE = 6;730const PENDING_GESTURE_ANIMATION_PHASE = 7;731let pendingEffectsStatus: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 = 0;732let pendingEffectsRoot: FiberRoot = null as any;733let pendingFinishedWork: Fiber = null as any;734let pendingEffectsLanes: Lanes = NoLanes;735let pendingEffectsRemainingLanes: Lanes = NoLanes;736let pendingEffectsRenderEndTime: number = -0; // Profiling-only737let pendingPassiveTransitions: Array<Transition> | null = null;738let pendingRecoverableErrors: null | Array<CapturedValue<mixed>> = null;739let pendingViewTransition: null | RunningViewTransition = null;740let pendingViewTransitionEvents: Array<741  (types: Array<string>) => void | (() => void),742> | null = null;743let pendingTransitionTypes: null | TransitionTypes = null;744let pendingDidIncludeRenderPhaseUpdate: boolean = false;745let pendingSuspendedCommitReason: SuspendedCommitReason = null; // Profiling-only746let pendingDelayedCommitReason: DelayedCommitReason = IMMEDIATE_COMMIT; // Profiling-only747let pendingSuspendedViewTransitionReason: null | string = null; // Profiling-only748749// Use these to prevent an infinite loop of nested updates750const NESTED_UPDATE_LIMIT = 50;751let nestedUpdateCount: number = 0;752let rootWithNestedUpdates: FiberRoot | null = null;753let isFlushingPassiveEffects = false;754let didScheduleUpdateDuringPassiveEffects = false;755756const NO_NESTED_UPDATE = 0;757const NESTED_UPDATE_SYNC_LANE = 1;758const NESTED_UPDATE_PHASE_SPAWN = 2;759let nestedUpdateKind: 0 | 1 | 2 = NO_NESTED_UPDATE;760761const NESTED_PASSIVE_UPDATE_LIMIT = 50;762let nestedPassiveUpdateCount: number = 0;763let rootWithPassiveNestedUpdates: FiberRoot | null = null;764765let isRunningInsertionEffect = false;766767export function getWorkInProgressRoot(): FiberRoot | null {768  return workInProgressRoot;769}770771export function getCommittingRoot(): FiberRoot | null {772  return pendingEffectsRoot;773}774775export function getWorkInProgressRootRenderLanes(): Lanes {776  return workInProgressRootRenderLanes;777}778779export function hasPendingCommitEffects(): boolean {780  return (781    pendingEffectsStatus !== NO_PENDING_EFFECTS &&782    pendingEffectsStatus !== PENDING_PASSIVE_PHASE783  );784}785786export function getRootWithPendingPassiveEffects(): FiberRoot | null {787  return pendingEffectsStatus === PENDING_PASSIVE_PHASE788    ? pendingEffectsRoot789    : null;790}791792export function getPendingPassiveEffectsLanes(): Lanes {793  return pendingEffectsLanes;794}795796export function getPendingTransitionTypes(): null | TransitionTypes {797  return pendingTransitionTypes;798}799800export function isWorkLoopSuspendedOnData(): boolean {801  return (802    workInProgressSuspendedReason === SuspendedOnData ||803    workInProgressSuspendedReason === SuspendedOnAction804  );805}806807export function getCurrentTime(): number {808  return now();809}810811export function requestUpdateLane(fiber: Fiber): Lane {812  // Special cases813  const mode = fiber.mode;814  if (!disableLegacyMode && (mode & ConcurrentMode) === NoMode) {815    return SyncLane as Lane;816  } else if (817    (executionContext & RenderContext) !== NoContext &&818    workInProgressRootRenderLanes !== NoLanes819  ) {820    // This is a render phase update. These are not officially supported. The821    // old behavior is to give this the same "thread" (lanes) as822    // whatever is currently rendering. So if you call `setState` on a component823    // that happens later in the same render, it will flush. Ideally, we want to824    // remove the special case and treat them as if they came from an825    // interleaved event. Regardless, this pattern is not officially supported.826    // This behavior is only a fallback. The flag only exists until we can roll827    // out the setState warning, since existing code might accidentally rely on828    // the current behavior.829    return pickArbitraryLane(workInProgressRootRenderLanes);830  }831832  const transition = requestCurrentTransition();833  if (transition !== null) {834    if (enableGestureTransition) {835      if (transition.gesture) {836        throw new Error(837          'Cannot setState on regular state inside a startGestureTransition. ' +838            'Gestures can only update the useOptimistic() hook. There should be no ' +839            'side-effects associated with starting a Gesture until its Action is ' +840            'invoked. Move side-effects to the Action instead.',841        );842      }843    }844    if (__DEV__) {845      if (!transition._updatedFibers) {846        transition._updatedFibers = new Set();847      }848      transition._updatedFibers.add(fiber);849      if (850        hasPotentialUseWarnings() &&851        resolveUpdatePriority() === DiscreteEventPriority852      ) {853        // If we're updating inside a discrete event, then this might be a new user interaction854        // and not just an automatically resolved loading sequence. Don't warn unless it happens again.855        clearUseWarnings();856      }857    }858859    return requestTransitionLane(transition);860  }861862  const priority = resolveUpdatePriority();863  if (__DEV__ && priority === DiscreteEventPriority) {864    clearUseWarnings();865  }866  return eventPriorityToLane(priority);867}868869function requestRetryLane(fiber: Fiber) {870  // This is a fork of `requestUpdateLane` designed specifically for Suspense871  // "retries" — a special update that attempts to flip a Suspense boundary872  // from its placeholder state to its primary/resolved state.873874  // Special cases875  const mode = fiber.mode;876  if (!disableLegacyMode && (mode & ConcurrentMode) === NoMode) {877    return SyncLane as Lane;878  }879880  return claimNextRetryLane();881}882883export function requestDeferredLane(): Lane {884  if (workInProgressDeferredLane === NoLane) {885    // If there are multiple useDeferredValue hooks in the same render, the886    // tasks that they spawn should all be batched together, so they should all887    // receive the same lane.888889    // Check the priority of the current render to decide the priority of the890    // deferred task.891892    // OffscreenLane is used for prerendering, but we also use OffscreenLane893    // for incremental hydration. It's given the lowest priority because the894    // initial HTML is the same as the final UI. But useDeferredValue during895    // hydration is an exception — we need to upgrade the UI to the final896    // value. So if we're currently hydrating, we treat it like a transition.897    const isPrerendering =898      includesSomeLane(workInProgressRootRenderLanes, OffscreenLane) &&899      !getIsHydrating();900    if (isPrerendering) {901      // There's only one OffscreenLane, so if it contains deferred work, we902      // should just reschedule using the same lane.903      workInProgressDeferredLane = OffscreenLane;904    } else {905      // Everything else is spawned as a transition.906      workInProgressDeferredLane = claimNextTransitionDeferredLane();907    }908  }909910  // Mark the parent Suspense boundary so it knows to spawn the deferred lane.911  const suspenseHandler = getSuspenseHandler();912  if (suspenseHandler !== null) {913    // TODO: As an optimization, we shouldn't entangle the lanes at the root; we914    // can entangle them using the baseLanes of the Suspense boundary instead.915    // We only need to do something special if there's no Suspense boundary.916    suspenseHandler.flags |= DidDefer;917  }918919  return workInProgressDeferredLane;920}921922export function scheduleViewTransitionEvent(923  fiber: Fiber,924  callback: ?(925    instance: ViewTransitionInstance,926    types: Array<string>,927  ) => void | (() => void),928): void {929  if (enableViewTransition) {930    if (callback != null) {931      const state: ViewTransitionState = fiber.stateNode;932      let instance = state.ref;933      if (instance === null) {934        instance = state.ref = createViewTransitionInstance(935          getViewTransitionName(fiber.memoizedProps, state),936        );937      }938      if (pendingViewTransitionEvents === null) {939        pendingViewTransitionEvents = [];940      }941      pendingViewTransitionEvents.push(callback.bind(null, instance));942    }943  }944}945946export function scheduleGestureTransitionEvent(947  fiber: Fiber,948  callback: ?(949    timeline: GestureTimeline,950    options: GestureOptionsRequired,951    instance: ViewTransitionInstance,952    types: Array<string>,953  ) => void | (() => void),954): void {955  if (enableGestureTransition) {956    if (callback != null) {957      const applyingGesture = pendingEffectsRoot.pendingGestures;958      if (applyingGesture !== null) {959        const state: ViewTransitionState = fiber.stateNode;960        let instance = state.ref;961        if (instance === null) {962          instance = state.ref = createViewTransitionInstance(963            getViewTransitionName(fiber.memoizedProps, state),964          );965        }966        const timeline = applyingGesture.provider;967        const options = {968          rangeStart: applyingGesture.rangeStart,969          rangeEnd: applyingGesture.rangeEnd,970        };971        if (pendingViewTransitionEvents === null) {972          pendingViewTransitionEvents = [];973        }974        pendingViewTransitionEvents.push(975          callback.bind(null, timeline, options, instance),976        );977      }978    }979  }980}981982export function peekDeferredLane(): Lane {983  return workInProgressDeferredLane;984}985986export function scheduleUpdateOnFiber(987  root: FiberRoot,988  fiber: Fiber,989  lane: Lane,990) {991  if (__DEV__) {992    if (isRunningInsertionEffect) {993      console.error('useInsertionEffect must not schedule updates.');994    }995  }996997  if (__DEV__) {998    if (isFlushingPassiveEffects) {999      didScheduleUpdateDuringPassiveEffects = true;1000    }1001  }10021003  // Check if the work loop is currently suspended and waiting for data to1004  // finish loading.1005  if (1006    // Suspended render phase1007    (root === workInProgressRoot &&1008      (workInProgressSuspendedReason === SuspendedOnData ||1009        workInProgressSuspendedReason === SuspendedOnAction)) ||1010    // Suspended commit phase1011    root.cancelPendingCommit !== null1012  ) {1013    // The incoming update might unblock the current render. Interrupt the1014    // current attempt and restart from the top.1015    prepareFreshStack(root, NoLanes);1016    const didAttemptEntireTree = false;1017    markRootSuspended(1018      root,1019      workInProgressRootRenderLanes,1020      workInProgressDeferredLane,1021      didAttemptEntireTree,1022    );1023  }10241025  // Mark that the root has a pending update.1026  markRootUpdated(root, lane);10271028  if (1029    (executionContext & RenderContext) !== NoContext &&1030    root === workInProgressRoot1031  ) {1032    // This update was dispatched during the render phase. This is a mistake1033    // if the update originates from user space (with the exception of local1034    // hook updates, which are handled differently and don't reach this1035    // function), but there are some internal React features that use this as1036    // an implementation detail, like selective hydration.1037    warnAboutRenderPhaseUpdatesInDEV(fiber);10381039    // Track lanes that were updated during the render phase1040    workInProgressRootRenderPhaseUpdatedLanes = mergeLanes(1041      workInProgressRootRenderPhaseUpdatedLanes,1042      lane,1043    );1044  } else {1045    // This is a normal update, scheduled from outside the render phase. For1046    // example, during an input event.1047    if (enableUpdaterTracking) {1048      if (isDevToolsPresent) {1049        addFiberToLanesMap(root, fiber, lane);1050      }1051    }10521053    warnIfUpdatesNotWrappedWithActDEV(fiber);10541055    if (enableTransitionTracing) {1056      const transition = ReactSharedInternals.T;1057      if (transition !== null && transition.name != null) {1058        if (transition.startTime === -1) {1059          transition.startTime = now();1060        }10611062        addTransitionToLanesMap(root, transition, lane);1063      }1064    }10651066    if (root === workInProgressRoot) {1067      // Received an update to a tree that's in the middle of rendering. Mark1068      // that there was an interleaved update work on this root.1069      if ((executionContext & RenderContext) === NoContext) {1070        workInProgressRootInterleavedUpdatedLanes = mergeLanes(1071          workInProgressRootInterleavedUpdatedLanes,1072          lane,1073        );1074      }1075      if (workInProgressRootExitStatus === RootSuspendedWithDelay) {1076        // The root already suspended with a delay, which means this render1077        // definitely won't finish. Since we have a new update, let's mark it as1078        // suspended now, right before marking the incoming update. This has the1079        // effect of interrupting the current render and switching to the update.1080        // TODO: Make sure this doesn't override pings that happen while we've1081        // already started rendering.1082        const didAttemptEntireTree = false;1083        markRootSuspended(1084          root,1085          workInProgressRootRenderLanes,1086          workInProgressDeferredLane,1087          didAttemptEntireTree,1088        );1089      }1090    }10911092    ensureRootIsScheduled(root);1093    if (1094      lane === SyncLane &&1095      executionContext === NoContext &&1096      !disableLegacyMode &&1097      (fiber.mode & ConcurrentMode) === NoMode1098    ) {1099      if (__DEV__ && ReactSharedInternals.isBatchingLegacy) {1100        // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.1101      } else {1102        // Flush the synchronous work now, unless we're already working or inside1103        // a batch. This is intentionally inside scheduleUpdateOnFiber instead of1104        // scheduleCallbackForFiber to preserve the ability to schedule a callback1105        // without immediately flushing it. We only do this for user-initiated1106        // updates, to preserve historical behavior of legacy mode.1107        resetRenderTimer();1108        flushSyncWorkOnLegacyRootsOnly();1109      }1110    }1111  }1112}11131114export function scheduleInitialHydrationOnRoot(root: FiberRoot, lane: Lane) {1115  // This is a special fork of scheduleUpdateOnFiber that is only used to1116  // schedule the initial hydration of a root that has just been created. Most1117  // of the stuff in scheduleUpdateOnFiber can be skipped.1118  //1119  // The main reason for this separate path, though, is to distinguish the1120  // initial children from subsequent updates. In fully client-rendered roots1121  // (createRoot instead of hydrateRoot), all top-level renders are modeled as1122  // updates, but hydration roots are special because the initial render must1123  // match what was rendered on the server.1124  const current = root.current;1125  current.lanes = lane;1126  markRootUpdated(root, lane);1127  ensureRootIsScheduled(root);1128}11291130export function isUnsafeClassRenderPhaseUpdate(fiber: Fiber): boolean {1131  // Check if this is a render phase update. Only called by class components,1132  // which special (deprecated) behavior for UNSAFE_componentWillReceive props.1133  return (executionContext & RenderContext) !== NoContext;1134}11351136export function performWorkOnRoot(1137  root: FiberRoot,1138  lanes: Lanes,1139  forceSync: boolean,1140): void {1141  if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {1142    throw new Error('Should not already be working.');1143  }11441145  if (enableProfilerTimer && enableComponentPerformanceTrack) {1146    if (workInProgressRootRenderLanes !== NoLanes && workInProgress !== null) {1147      const yieldedFiber = workInProgress;1148      // We've returned from yielding to the event loop. Let's log the time it took.1149      const yieldEndTime = now();1150      switch (yieldReason) {1151        case SuspendedOnImmediate:1152        case SuspendedOnData:1153          logSuspendedYieldTime(yieldStartTime, yieldEndTime, yieldedFiber);1154          break;1155        case SuspendedOnAction:1156          logActionYieldTime(yieldStartTime, yieldEndTime, yieldedFiber);1157          break;1158        default:1159          logYieldTime(yieldStartTime, yieldEndTime);1160      }1161    }1162  }11631164  // We disable time-slicing in some cases: if the work has been CPU-bound1165  // for too long ("expired" work, to prevent starvation), or we're in1166  // sync-updates-by-default mode.1167  const shouldTimeSlice =1168    (!forceSync &&1169      !includesBlockingLane(lanes) &&1170      !includesExpiredLane(root, lanes)) ||1171    // If we're prerendering, then we should use the concurrent work loop1172    // even if the lanes are synchronous, so that prerendering never blocks1173    // the main thread.1174    // TODO: We should consider doing this whenever a sync lane is suspended,1175    // even for regular pings.1176    checkIfRootIsPrerendering(root, lanes);11771178  let exitStatus: RootExitStatus = shouldTimeSlice1179    ? renderRootConcurrent(root, lanes)1180    : renderRootSync(root, lanes, true);11811182  let renderWasConcurrent = shouldTimeSlice;11831184  do {1185    if (exitStatus === RootInProgress) {1186      // Render phase is still in progress.1187      if (workInProgressRootIsPrerendering && !shouldTimeSlice) {1188        // We're in prerendering mode, but time slicing is not enabled. This1189        // happens when something suspends during a synchronous update. Exit the1190        // the work loop. When we resume, we'll use the concurrent work loop so1191        // that prerendering is non-blocking.1192        //1193        // Mark the root as suspended. Usually we do this at the end of the1194        // render phase, but we do it here so that we resume in1195        // prerendering mode.1196        // TODO: Consider always calling markRootSuspended immediately.1197        // Needs to be *after* we attach a ping listener, though.1198        const didAttemptEntireTree = false;1199        markRootSuspended(root, lanes, NoLane, didAttemptEntireTree);1200      }1201      if (enableProfilerTimer && enableComponentPerformanceTrack) {1202        // We're about to yield. Let's keep track of how long we yield to the event loop.1203        // We also stash the suspended reason at the time we yielded since it might have1204        // changed when we resume such as when it gets pinged.1205        startYieldTimer(workInProgressSuspendedReason);1206      }1207      break;1208    } else {1209      let renderEndTime = 0;1210      if (enableProfilerTimer && enableComponentPerformanceTrack) {1211        renderEndTime = now();1212      }12131214      // The render completed.12151216      // Check if this render may have yielded to a concurrent event, and if so,1217      // confirm that any newly rendered stores are consistent.1218      // TODO: It's possible that even a concurrent render may never have yielded1219      // to the main thread, if it was fast enough, or if it expired. We could1220      // skip the consistency check in that case, too.1221      const finishedWork: Fiber = root.current.alternate as any;1222      if (1223        renderWasConcurrent &&1224        !isRenderConsistentWithExternalStores(finishedWork)1225      ) {1226        if (enableProfilerTimer && enableComponentPerformanceTrack) {1227          setCurrentTrackFromLanes(lanes);1228          logInconsistentRender(1229            renderStartTime,1230            renderEndTime,1231            workInProgressUpdateTask,1232          );1233          finalizeRender(lanes, renderEndTime);1234        }1235        // A store was mutated in an interleaved event. Render again,1236        // synchronously, to block further mutations.1237        exitStatus = renderRootSync(root, lanes, false);1238        // We assume the tree is now consistent because we didn't yield to any1239        // concurrent events.1240        renderWasConcurrent = false;1241        // Need to check the exit status again.1242        continue;1243      }12441245      // Check if something threw1246      if (1247        (disableLegacyMode || root.tag !== LegacyRoot) &&1248        exitStatus === RootErrored1249      ) {1250        const lanesThatJustErrored = lanes;1251        const errorRetryLanes = getLanesToRetrySynchronouslyOnError(1252          root,1253          lanesThatJustErrored,1254        );1255        if (errorRetryLanes !== NoLanes) {1256          if (enableProfilerTimer && enableComponentPerformanceTrack) {1257            setCurrentTrackFromLanes(lanes);1258            logErroredRenderPhase(1259              renderStartTime,1260              renderEndTime,1261              lanes,1262              workInProgressUpdateTask,1263            );1264            finalizeRender(lanes, renderEndTime);1265          }1266          lanes = errorRetryLanes;1267          exitStatus = recoverFromConcurrentError(1268            root,1269            lanesThatJustErrored,1270            errorRetryLanes,1271          );1272          renderWasConcurrent = false;1273          // Need to check the exit status again.1274          if (exitStatus !== RootErrored) {1275            // The root did not error this time. Restart the exit algorithm1276            // from the beginning.1277            // TODO: Refactor the exit algorithm to be less confusing. Maybe1278            // more branches + recursion instead of a loop. I think the only1279            // thing that causes it to be a loop is the RootSuspendedAtTheShell1280            // check. If that's true, then we don't need a loop/recursion1281            // at all.1282            continue;1283          } else {1284            // The root errored yet again. Proceed to commit the tree.1285            if (enableProfilerTimer && enableComponentPerformanceTrack) {1286              renderEndTime = now();1287            }1288          }1289        }1290      }1291      if (exitStatus === RootFatalErrored) {1292        if (enableProfilerTimer && enableComponentPerformanceTrack) {1293          setCurrentTrackFromLanes(lanes);1294          logErroredRenderPhase(1295            renderStartTime,1296            renderEndTime,1297            lanes,1298            workInProgressUpdateTask,1299          );1300          finalizeRender(lanes, renderEndTime);1301        }1302        prepareFreshStack(root, NoLanes);1303        // Since this is a fatal error, we're going to pretend we attempted1304        // the entire tree, to avoid scheduling a prerender.1305        const didAttemptEntireTree = true;1306        markRootSuspended(root, lanes, NoLane, didAttemptEntireTree);1307        break;1308      }13091310      // We now have a consistent tree. The next step is either to commit it,1311      // or, if something suspended, wait to commit it after a timeout.1312      finishConcurrentRender(1313        root,1314        exitStatus,1315        finishedWork,1316        lanes,1317        renderEndTime,1318      );1319    }1320    break;1321  } while (true);13221323  ensureRootIsScheduled(root);1324}13251326function recoverFromConcurrentError(1327  root: FiberRoot,1328  originallyAttemptedLanes: Lanes,1329  errorRetryLanes: Lanes,1330): RootExitStatus {1331  // If an error occurred during hydration, discard server response and fall1332  // back to client side render.13331334  // Before rendering again, save the errors from the previous attempt.1335  const errorsFromFirstAttempt = workInProgressRootConcurrentErrors;13361337  const wasRootDehydrated = supportsHydration && isRootDehydrated(root);1338  if (wasRootDehydrated) {1339    // The shell failed to hydrate. Set a flag to force a client rendering1340    // during the next attempt. To do this, we call prepareFreshStack now1341    // to create the root work-in-progress fiber. This is a bit weird in terms1342    // of factoring, because it relies on renderRootSync not calling1343    // prepareFreshStack again in the call below, which happens because the1344    // root and lanes haven't changed.1345    //1346    // TODO: I think what we should do is set ForceClientRender inside1347    // throwException, like we do for nested Suspense boundaries. The reason1348    // it's here instead is so we can switch to the synchronous work loop, too.1349    // Something to consider for a future refactor.1350    const rootWorkInProgress = prepareFreshStack(root, errorRetryLanes);1351    rootWorkInProgress.flags |= ForceClientRender;1352  }13531354  const exitStatus = renderRootSync(root, errorRetryLanes, false);1355  if (exitStatus === RootSuspendedAtTheShell) {1356    // The retry unwound to the root without completing (e.g. something1357    // suspended in the shell), so the tree is incomplete and must not be1358    // treated as recovered — committing it would corrupt the current tree.1359    // Return the status as-is so the root stays suspended.1360    //1361    // On a dehydrated root, the retry is the client render, not a check for1362    // data races, so we leave error recovery enabled.1363    if (!wasRootDehydrated) {1364      // The retry couldn't finish, so it can't tell us whether the error was1365      // caused by a data race. Disable error recovery for these lanes so the1366      // next attempt commits the error boundary instead of retrying again.1367      // That includes any deferred lane the retry spawned, which would1368      // otherwise error and retry the same way.1369      root.errorRecoveryDisabledLanes = mergeLanes(1370        root.errorRecoveryDisabledLanes,1371        mergeLanes(originallyAttemptedLanes, workInProgressDeferredLane),1372      );1373    }1374  } else if (exitStatus !== RootErrored) {1375    // Successfully finished rendering on retry13761377    if (workInProgressRootDidAttachPingListener && !wasRootDehydrated) {1378      // During the synchronous render, we attached additional ping listeners.1379      // This is highly suggestive of an uncached promise (though it's not the1380      // only reason this would happen). If it was an uncached promise, then1381      // it may have masked a downstream error from ocurring without actually1382      // fixing it. Example:1383      //1384      //    use(Promise.resolve('uncached'))1385      //    throw new Error('Oops!')1386      //1387      // When this happens, there's a conflict between blocking potential1388      // concurrent data races and unwrapping uncached promise values. We1389      // have to choose one or the other. Because the data race recovery is1390      // a last ditch effort, we'll disable it.1391      root.errorRecoveryDisabledLanes = mergeLanes(1392        root.errorRecoveryDisabledLanes,1393        originallyAttemptedLanes,1394      );13951396      // Mark the current render as suspended and force it to restart. Once1397      // these lanes finish successfully, we'll re-enable the error recovery1398      // mechanism for subsequent updates.1399      workInProgressRootInterleavedUpdatedLanes |= originallyAttemptedLanes;1400      return RootSuspendedWithDelay;1401    }14021403    // The errors from the failed first attempt have been recovered. Add1404    // them to the collection of recoverable errors. We'll log them in the1405    // commit phase.1406    const errorsFromSecondAttempt = workInProgressRootRecoverableErrors;1407    workInProgressRootRecoverableErrors = errorsFromFirstAttempt;1408    // The errors from the second attempt should be queued after the errors1409    // from the first attempt, to preserve the causal sequence.1410    if (errorsFromSecondAttempt !== null) {1411      queueRecoverableErrors(errorsFromSecondAttempt);1412    }1413  } else {1414    // The UI failed to recover.1415  }1416  return exitStatus;1417}14181419export function queueRecoverableErrors(errors: Array<CapturedValue<mixed>>) {1420  if (workInProgressRootRecoverableErrors === null) {1421    workInProgressRootRecoverableErrors = errors;1422  } else {1423    // $FlowFixMe[method-unbinding]1424    workInProgressRootRecoverableErrors.push.apply(1425      workInProgressRootRecoverableErrors,1426      errors,1427    );1428  }1429}14301431function finishConcurrentRender(1432  root: FiberRoot,1433  exitStatus: RootExitStatus,1434  finishedWork: Fiber,1435  lanes: Lanes,1436  renderEndTime: number, // Profiling-only1437) {1438  // TODO: The fact that most of these branches are identical suggests that some1439  // of the exit statuses are not best modeled as exit statuses and should be1440  // tracked orthogonally.1441  switch (exitStatus) {1442    case RootInProgress:1443    case RootFatalErrored: {1444      throw new Error('Root did not complete. This is a bug in React.');1445    }1446    case RootSuspendedWithDelay: {1447      if (!includesOnlyTransitions(lanes) && !includesOnlyRetries(lanes)) {1448        // Commit the placeholder.1449        break;1450      }1451    }1452    // Fallthrough1453    case RootSuspendedAtTheShell: {1454      // This is a transition, so we should exit without committing a1455      // placeholder and without scheduling a timeout. Delay indefinitely1456      // until we receive more data.1457      if (enableProfilerTimer && enableComponentPerformanceTrack) {1458        setCurrentTrackFromLanes(lanes);1459        logSuspendedRenderPhase(1460          renderStartTime,1461          renderEndTime,1462          lanes,1463          workInProgressUpdateTask,1464        );1465        finalizeRender(lanes, renderEndTime);1466        trackSuspendedTime(lanes, renderEndTime);1467      }1468      const didAttemptEntireTree = !workInProgressRootDidSkipSuspendedSiblings;1469      markRootSuspended(1470        root,1471        lanes,1472        workInProgressDeferredLane,1473        didAttemptEntireTree,1474      );1475      return;1476    }1477    case RootErrored: {1478      // This render errored. Ignore any recoverable errors because we weren't actually1479      // able to recover. Instead, whatever the final errors were is the ones we log.1480      // This ensures that we only log the actual client side error if it's just a plain1481      // error thrown from a component on the server and the client.1482      workInProgressRootRecoverableErrors = null;1483      break;1484    }1485    case RootSuspended:1486    case RootCompleted: {1487      break;1488    }1489    default: {1490      throw new Error('Unknown root exit status.');1491    }1492  }14931494  if (shouldForceFlushFallbacksInDEV()) {1495    // We're inside an `act` scope. Commit immediately.1496    completeRoot(1497      root,1498      finishedWork,1499      lanes,1500      workInProgressRootRecoverableErrors,1501      workInProgressTransitions,1502      workInProgressRootDidIncludeRecursiveRenderUpdate,1503      workInProgressDeferredLane,1504      workInProgressRootInterleavedUpdatedLanes,1505      workInProgressSuspendedRetryLanes,1506      workInProgressRootDidSkipSuspendedSiblings,1507      exitStatus,1508      null,1509      null,1510      renderStartTime,1511      renderEndTime,1512    );1513  } else {1514    if (1515      includesOnlyRetries(lanes) &&1516      (alwaysThrottleRetries || exitStatus === RootSuspended)1517    ) {1518      // This render only included retries, no updates. Throttle committing1519      // retries so that we don't show too many loading states too quickly.1520      const msUntilTimeout =1521        globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();15221523      // Don't bother with a very short suspense time.1524      if (msUntilTimeout > 10) {1525        const didAttemptEntireTree =1526          !workInProgressRootDidSkipSuspendedSiblings;1527        markRootSuspended(1528          root,1529          lanes,1530          workInProgressDeferredLane,1531          didAttemptEntireTree,1532        );15331534        const nextLanes = getNextLanes(root, NoLanes, true);1535        if (nextLanes !== NoLanes) {1536          // There's additional work we can do on this root. We might as well1537          // attempt to work on that while we're suspended.1538          return;1539        }15401541        // The render is suspended, it hasn't timed out, and there's no1542        // lower priority work to do. Instead of committing the fallback1543        // immediately, wait for more data to arrive.1544        // TODO: Combine retry throttling with Suspensey commits. Right now they1545        // run one after the other.1546        pendingEffectsLanes = lanes;1547        root.timeoutHandle = scheduleTimeout(1548          completeRootWhenReady.bind(1549            null,1550            root,1551            finishedWork,1552            workInProgressRootRecoverableErrors,1553            workInProgressTransitions,1554            workInProgressRootDidIncludeRecursiveRenderUpdate,1555            lanes,1556            workInProgressDeferredLane,1557            workInProgressRootInterleavedUpdatedLanes,1558            workInProgressSuspendedRetryLanes,1559            workInProgressRootDidSkipSuspendedSiblings,1560            exitStatus,1561            'Throttled',1562            renderStartTime,1563            renderEndTime,1564          ),1565          msUntilTimeout,1566        );1567        return;1568      }1569    }1570    completeRootWhenReady(1571      root,1572      finishedWork,1573      workInProgressRootRecoverableErrors,1574      workInProgressTransitions,1575      workInProgressRootDidIncludeRecursiveRenderUpdate,1576      lanes,1577      workInProgressDeferredLane,1578      workInProgressRootInterleavedUpdatedLanes,1579      workInProgressSuspendedRetryLanes,1580      workInProgressRootDidSkipSuspendedSiblings,1581      exitStatus,1582      null,1583      renderStartTime,1584      renderEndTime,1585    );1586  }1587}15881589function completeRootWhenReady(1590  root: FiberRoot,1591  finishedWork: Fiber,1592  recoverableErrors: Array<CapturedValue<mixed>> | null,1593  transitions: Array<Transition> | null,1594  didIncludeRenderPhaseUpdate: boolean,1595  lanes: Lanes,1596  spawnedLane: Lane,1597  updatedLanes: Lanes,1598  suspendedRetryLanes: Lanes,1599  didSkipSuspendedSiblings: boolean,1600  exitStatus: RootExitStatus,1601  suspendedCommitReason: SuspendedCommitReason, // Profiling-only1602  completedRenderStartTime: number, // Profiling-only1603  completedRenderEndTime: number, // Profiling-only1604) {1605  root.timeoutHandle = noTimeout;16061607  // TODO: Combine retry throttling with Suspensey commits. Right now they run1608  // one after the other.1609  const BothVisibilityAndMaySuspendCommit = Visibility | MaySuspendCommit;1610  const subtreeFlags = finishedWork.subtreeFlags;1611  const isViewTransitionEligible =1612    enableViewTransition && includesOnlyViewTransitionEligibleLanes(lanes); // TODO: Use a subtreeFlag to optimize.1613  const isGestureTransition = enableGestureTransition && isGestureRender(lanes);1614  const maySuspendCommit =1615    subtreeFlags & ShouldSuspendCommit ||1616    (subtreeFlags & BothVisibilityAndMaySuspendCommit) ===1617      BothVisibilityAndMaySuspendCommit;1618  let suspendedState: null | SuspendedState = null;1619  if (isViewTransitionEligible || maySuspendCommit || isGestureTransition) {1620    // Before committing, ask the renderer whether the host tree is ready.1621    // If it's not, we'll wait until it notifies us.1622    suspendedState = startSuspendingCommit();1623    // This will walk the completed fiber tree and attach listeners to all1624    // the suspensey resources. The renderer is responsible for accumulating1625    // all the load events. This all happens in a single synchronous1626    // transaction, so it track state in its own module scope.1627    // This will also track any newly added or appearing ViewTransition1628    // components for the purposes of forming pairs.1629    accumulateSuspenseyCommit(finishedWork, lanes, suspendedState);1630    if (1631      isViewTransitionEligible ||1632      (isGestureTransition &&1633        root.pendingGestures !== null &&1634        // If this gesture already has a View Transition running then we don't1635        // have to wait on that one before proceeding. We may hold the commit1636        // on the gesture committing later on in completeRoot.1637        root.pendingGestures.running === null)1638    ) {1639      // Wait for any pending View Transition (including gestures) to finish.1640      suspendOnActiveViewTransition(suspendedState, root.containerInfo);1641    }1642    // For timeouts we use the previous fallback commit for retries and1643    // the start time of the transition for transitions. This offset1644    // represents the time already passed.1645    const timeoutOffset = includesOnlyRetries(lanes)1646      ? globalMostRecentFallbackTime - now()1647      : includesOnlyTransitions(lanes)1648        ? globalMostRecentTransitionTime - now()1649        : 0;1650    // At the end, ask the renderer if it's ready to commit, or if we should1651    // suspend. If it's not ready, it will return a callback to subscribe to1652    // a ready event.1653    const schedulePendingCommit = waitForCommitToBeReady(1654      suspendedState,1655      timeoutOffset,1656    );1657    if (schedulePendingCommit !== null) {1658      // NOTE: waitForCommitToBeReady returns a subscribe function so that we1659      // only allocate a function if the commit isn't ready yet. The other1660      // pattern would be to always pass a callback to waitForCommitToBeReady.16611662      // Not yet ready to commit. Delay the commit until the renderer notifies1663      // us that it's ready. This will be canceled if we start work on the1664      // root again.1665      pendingEffectsLanes = lanes;1666      root.cancelPendingCommit = schedulePendingCommit(1667        completeRoot.bind(1668          null,1669          root,1670          finishedWork,1671          lanes,1672          recoverableErrors,1673          transitions,1674          didIncludeRenderPhaseUpdate,1675          spawnedLane,1676          updatedLanes,1677          suspendedRetryLanes,1678          didSkipSuspendedSiblings,1679          exitStatus,1680          suspendedState,1681          enableProfilerTimer1682            ? getSuspendedCommitReason(suspendedState, root.containerInfo)1683            : null,1684          completedRenderStartTime,1685          completedRenderEndTime,1686        ),1687      );1688      const didAttemptEntireTree = !didSkipSuspendedSiblings;1689      markRootSuspended(root, lanes, spawnedLane, didAttemptEntireTree);1690      return;1691    }1692  }16931694  // Otherwise, commit immediately.;1695  completeRoot(1696    root,1697    finishedWork,1698    lanes,1699    recoverableErrors,1700    transitions,1701    didIncludeRenderPhaseUpdate,1702    spawnedLane,1703    updatedLanes,1704    suspendedRetryLanes,1705    didSkipSuspendedSiblings,1706    exitStatus,1707    suspendedState,1708    suspendedCommitReason,1709    completedRenderStartTime,1710    completedRenderEndTime,1711  );1712}17131714function isRenderConsistentWithExternalStores(finishedWork: Fiber): boolean {1715  // Search the rendered tree for external store reads, and check whether the1716  // stores were mutated in a concurrent event. Intentionally using an iterative1717  // loop instead of recursion so we can exit early.1718  let node: Fiber = finishedWork;1719  while (true) {1720    const tag = node.tag;1721    if (1722      (tag === FunctionComponent ||1723        tag === ForwardRef ||1724        tag === SimpleMemoComponent) &&1725      node.flags & StoreConsistency1726    ) {1727      const updateQueue: FunctionComponentUpdateQueue | null =1728        node.updateQueue as any;1729      if (updateQueue !== null) {1730        const checks = updateQueue.stores;1731        if (checks !== null) {1732          for (let i = 0; i < checks.length; i++) {1733            const check = checks[i];1734            const getSnapshot = check.getSnapshot;1735            const renderedValue = check.value;1736            try {1737              if (!is(getSnapshot(), renderedValue)) {1738                // Found an inconsistent store.1739                return false;1740              }1741            } catch (error) {1742              // If `getSnapshot` throws, return `false`. This will schedule1743              // a re-render, and the error will be rethrown during render.1744              return false;1745            }1746          }1747        }1748      }1749    }1750    const child = node.child;1751    if (node.subtreeFlags & StoreConsistency && child !== null) {1752      child.return = node;1753      node = child;1754      continue;1755    }1756    if (node === finishedWork) {1757      return true;1758    }1759    while (node.sibling === null) {1760      if (node.return === null || node.return === finishedWork) {1761        return true;1762      }1763      node = node.return;1764    }1765    node.sibling.return = node.return;1766    node = node.sibling;1767  }1768  // Flow doesn't know this is unreachable, but eslint does1769  // eslint-disable-next-line no-unreachable1770  return true;1771}17721773// The extra indirections around markRootUpdated and markRootSuspended is1774// needed to avoid a circular dependency between this module and1775// ReactFiberLane. There's probably a better way to split up these modules and1776// avoid this problem. Perhaps all the root-marking functions should move into1777// the work loop.17781779function markRootUpdated(root: FiberRoot, updatedLanes: Lanes) {1780  _markRootUpdated(root, updatedLanes);17811782  if (enableInfiniteRenderLoopDetection) {1783    // Check for recursive updates1784    if (executionContext & RenderContext) {1785      workInProgressRootDidIncludeRecursiveRenderUpdate = true;1786    } else if (executionContext & CommitContext) {1787      didIncludeCommitPhaseUpdate = true;1788    }17891790    throwIfInfiniteUpdateLoopDetected(true);1791  }1792}17931794function markRootPinged(root: FiberRoot, pingedLanes: Lanes) {1795  _markRootPinged(root, pingedLanes);17961797  if (enableInfiniteRenderLoopDetection) {1798    // Check for recursive pings. Pings are conceptually different from updates in1799    // other contexts but we call it an "update" in this context because1800    // repeatedly pinging a suspended render can cause a recursive render loop.1801    // The relevant property is that it can result in a new render attempt1802    // being scheduled.1803    if (executionContext & RenderContext) {1804      workInProgressRootDidIncludeRecursiveRenderUpdate = true;1805    } else if (executionContext & CommitContext) {1806      didIncludeCommitPhaseUpdate = true;1807    }18081809    throwIfInfiniteUpdateLoopDetected(true);1810  }1811}18121813function markRootSuspended(1814  root: FiberRoot,1815  suspendedLanes: Lanes,1816  spawnedLane: Lane,1817  didAttemptEntireTree: boolean,1818) {1819  // When suspending, we should always mark the entangled lanes as suspended.1820  suspendedLanes = getEntangledLanes(root, suspendedLanes);18211822  // When suspending, we should always exclude lanes that were pinged or (more1823  // rarely, since we try to avoid it) updated during the render phase.1824  suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);1825  suspendedLanes = removeLanes(1826    suspendedLanes,1827    workInProgressRootInterleavedUpdatedLanes,1828  );1829  _markRootSuspended(root, suspendedLanes, spawnedLane, didAttemptEntireTree);1830}18311832export function flushRoot(root: FiberRoot, lanes: Lanes) {1833  if (lanes !== NoLanes) {1834    upgradePendingLanesToSync(root, lanes);1835    ensureRootIsScheduled(root);1836    if ((executionContext & (RenderContext | CommitContext)) === NoContext) {1837      resetRenderTimer();1838      // TODO: For historical reasons this flushes all sync work across all1839      // roots. It shouldn't really matter either way, but we could change this1840      // to only flush the given root.1841      flushSyncWorkOnAllRoots();1842    }1843  }1844}18451846export function getExecutionContext(): ExecutionContext {1847  return executionContext;1848}18491850export function deferredUpdates<A>(fn: () => A): A {1851  const prevTransition = ReactSharedInternals.T;1852  const previousPriority = getCurrentUpdatePriority();1853  try {1854    setCurrentUpdatePriority(DefaultEventPriority);1855    ReactSharedInternals.T = null;1856    return fn();1857  } finally {1858    setCurrentUpdatePriority(previousPriority);1859    ReactSharedInternals.T = prevTransition;1860  }1861}18621863export function batchedUpdates<A, R>(fn: A => R, a: A): R {1864  if (disableLegacyMode) {1865    // batchedUpdates is a no-op now, but there's still some internal react-dom1866    // code calling it, that we can't remove until we remove legacy mode.1867    return fn(a);1868  } else {1869    const prevExecutionContext = executionContext;1870    executionContext |= BatchedContext;1871    try {1872      return fn(a);1873    } finally {1874      executionContext = prevExecutionContext;1875      // If there were legacy sync updates, flush them at the end of the outer1876      // most batchedUpdates-like method.1877      if (1878        executionContext === NoContext &&1879        // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.1880        !(__DEV__ && ReactSharedInternals.isBatchingLegacy)1881      ) {1882        resetRenderTimer();1883        flushSyncWorkOnLegacyRootsOnly();1884      }1885    }1886  }1887}18881889export function discreteUpdates<A, B, C, D, R>(1890  fn: (A, B, C, D) => R,1891  a: A,1892  b: B,1893  c: C,1894  d: D,1895): R {1896  const prevTransition = ReactSharedInternals.T;1897  const previousPriority = getCurrentUpdatePriority();1898  try {1899    setCurrentUpdatePriority(DiscreteEventPriority);1900    ReactSharedInternals.T = null;1901    return fn(a, b, c, d);1902  } finally {1903    setCurrentUpdatePriority(previousPriority);1904    ReactSharedInternals.T = prevTransition;1905    if (executionContext === NoContext) {1906      resetRenderTimer();1907    }1908  }1909}19101911// Overload the definition to the two valid signatures.1912// Warning, this opts-out of checking the function body.1913declare function flushSyncFromReconciler<R>(fn: () => R): R;1914declare function flushSyncFromReconciler(void): void;1915export function flushSyncFromReconciler<R>(fn: (() => R) | void): R | void {1916  // In legacy mode, we flush pending passive effects at the beginning of the1917  // next event, not at the end of the previous one.1918  if (1919    pendingEffectsStatus !== NO_PENDING_EFFECTS &&1920    !disableLegacyMode &&1921    pendingEffectsRoot.tag === LegacyRoot &&1922    (executionContext & (RenderContext | CommitContext)) === NoContext1923  ) {1924    flushPendingEffects();1925  }19261927  const prevExecutionContext = executionContext;1928  executionContext |= BatchedContext;19291930  const prevTransition = ReactSharedInternals.T;1931  const previousPriority = getCurrentUpdatePriority();19321933  try {1934    setCurrentUpdatePriority(DiscreteEventPriority);1935    ReactSharedInternals.T = null;1936    if (fn) {1937      return fn();1938    } else {1939      return undefined;1940    }1941  } finally {1942    setCurrentUpdatePriority(previousPriority);1943    ReactSharedInternals.T = prevTransition;19441945    executionContext = prevExecutionContext;1946    // Flush the immediate callbacks that were scheduled during this batch.1947    // Note that this will happen even if batchedUpdates is higher up1948    // the stack.1949    if ((executionContext & (RenderContext | CommitContext)) === NoContext) {1950      flushSyncWorkOnAllRoots();1951    }1952  }1953}19541955// If called outside of a render or commit will flush all sync work on all roots1956// Returns whether the the call was during a render or not1957export function flushSyncWork(): boolean {1958  if ((executionContext & (RenderContext | CommitContext)) === NoContext) {1959    flushSyncWorkOnAllRoots();1960    return false;1961  }1962  return true;1963}19641965export function isAlreadyRendering(): boolean {1966  // Used by the renderer to print a warning if certain APIs are called from1967  // the wrong context, and for profiling warnings.1968  return (executionContext & (RenderContext | CommitContext)) !== NoContext;1969}19701971export function isInvalidExecutionContextForEventFunction(): boolean {1972  // Used to throw if certain APIs are called from the wrong context.1973  return (executionContext & RenderContext) !== NoContext;1974}19751976// This is called by the HiddenContext module when we enter or leave a1977// hidden subtree. The stack logic is managed there because that's the only1978// place that ever modifies it. Which module it lives in doesn't matter for1979// performance because this function will get inlined regardless1980export function setEntangledRenderLanes(newEntangledRenderLanes: Lanes) {1981  entangledRenderLanes = newEntangledRenderLanes;1982}19831984export function getEntangledRenderLanes(): Lanes {1985  return entangledRenderLanes;1986}19871988function resetWorkInProgressStack() {1989  if (workInProgress === null) return;1990  let interruptedWork;1991  if (workInProgressSuspendedReason === NotSuspended) {1992    // Normal case. Work-in-progress hasn't started yet. Unwind all1993    // its parents.1994    interruptedWork = workInProgress.return;1995  } else {1996    // Work-in-progress is in suspended state. Reset the work loop and unwind1997    // both the suspended fiber and all its parents.1998    resetSuspendedWorkLoopOnUnwind(workInProgress);1999    interruptedWork = workInProgress;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.

Generated by searchcode.com · powered by code_get_file + code_get_findings