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.