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