How to use getIsUpdatingOpaqueValueInRenderPhaseInDEV method in Playwright Internal

Best JavaScript code snippet using playwright-internal

Run Playwright Internal automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

ReactFiberWorkLoop.new.js

Source: ReactFiberWorkLoop.new.js Github

copy
1/**
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10import type {Wakeable} from 'shared/ReactTypes';
11import type {Fiber, FiberRoot} from './ReactInternalTypes';
12import type {Lanes, Lane} from './ReactFiberLane.new';
13import type {SuspenseState} from './ReactFiberSuspenseComponent.new';
14import type {StackCursor} from './ReactFiberStack.new';
15import type {Flags} from './ReactFiberFlags';
16
17import {
18  warnAboutDeprecatedLifecycles,
19  enableSuspenseServerRenderer,
20  replayFailedUnitOfWorkWithInvokeGuardedCallback,
21  enableCreateEventHandleAPI,
22  enableProfilerTimer,
23  enableProfilerCommitHooks,
24  enableProfilerNestedUpdatePhase,
25  enableProfilerNestedUpdateScheduledHook,
26  deferRenderPhaseUpdateToNextBatch,
27  enableDebugTracing,
28  enableSchedulingProfiler,
29  disableSchedulerTimeoutInWorkLoop,
30  enableStrictEffects,
31  skipUnmountedBoundaries,
32  enableUpdaterTracking,
33} from 'shared/ReactFeatureFlags';
34import ReactSharedInternals from 'shared/ReactSharedInternals';
35import invariant from 'shared/invariant';
36
37import {
38  // Aliased because `act` will override and push to an internal queue
39  scheduleCallback as Scheduler_scheduleCallback,
40  cancelCallback as Scheduler_cancelCallback,
41  shouldYield,
42  requestPaint,
43  now,
44  ImmediatePriority as ImmediateSchedulerPriority,
45  UserBlockingPriority as UserBlockingSchedulerPriority,
46  NormalPriority as NormalSchedulerPriority,
47  IdlePriority as IdleSchedulerPriority,
48} from './Scheduler';
49import {
50  flushSyncCallbacks,
51  flushSyncCallbacksOnlyInLegacyMode,
52  scheduleSyncCallback,
53  scheduleLegacySyncCallback,
54} from './ReactFiberSyncTaskQueue.new';
55import {
56  logCommitStarted,
57  logCommitStopped,
58  logLayoutEffectsStarted,
59  logLayoutEffectsStopped,
60  logPassiveEffectsStarted,
61  logPassiveEffectsStopped,
62  logRenderStarted,
63  logRenderStopped,
64} from './DebugTracing';
65import {
66  markCommitStarted,
67  markCommitStopped,
68  markLayoutEffectsStarted,
69  markLayoutEffectsStopped,
70  markPassiveEffectsStarted,
71  markPassiveEffectsStopped,
72  markRenderStarted,
73  markRenderYielded,
74  markRenderStopped,
75} from './SchedulingProfiler';
76
77import {
78  resetAfterCommit,
79  scheduleTimeout,
80  cancelTimeout,
81  noTimeout,
82  warnsIfNotActing,
83  afterActiveInstanceBlur,
84  clearContainer,
85  getCurrentEventPriority,
86  supportsMicrotasks,
87  errorHydratingContainer,
88  scheduleMicrotask,
89} from './ReactFiberHostConfig';
90
91import {
92  createWorkInProgress,
93  assignFiberPropertiesInDEV,
94} from './ReactFiber.new';
95import {
96  NoMode,
97  StrictLegacyMode,
98  ProfileMode,
99  ConcurrentMode,
100} from './ReactTypeOfMode';
101import {
102  HostRoot,
103  IndeterminateComponent,
104  ClassComponent,
105  SuspenseComponent,
106  SuspenseListComponent,
107  FunctionComponent,
108  ForwardRef,
109  MemoComponent,
110  SimpleMemoComponent,
111  Profiler,
112} from './ReactWorkTags';
113import {LegacyRoot} from './ReactRootTags';
114import {
115  NoFlags,
116  Placement,
117  Incomplete,
118  HostEffectMask,
119  Hydrating,
120  BeforeMutationMask,
121  MutationMask,
122  LayoutMask,
123  PassiveMask,
124  MountPassiveDev,
125  MountLayoutDev,
126} from './ReactFiberFlags';
127import {
128  NoLanes,
129  NoLane,
130  SyncLane,
131  NoTimestamp,
132  claimNextTransitionLane,
133  claimNextRetryLane,
134  includesSomeLane,
135  isSubsetOfLanes,
136  mergeLanes,
137  removeLanes,
138  pickArbitraryLane,
139  includesNonIdleWork,
140  includesOnlyRetries,
141  includesOnlyTransitions,
142  shouldTimeSlice,
143  getNextLanes,
144  markStarvedLanesAsExpired,
145  getLanesToRetrySynchronouslyOnError,
146  getMostRecentEventTime,
147  markRootUpdated,
148  markRootSuspended as markRootSuspended_dontCallThisOneDirectly,
149  markRootPinged,
150  markRootEntangled,
151  markRootFinished,
152  getHighestPriorityLane,
153  addFiberToLanesMap,
154  movePendingFibersToMemoized,
155} from './ReactFiberLane.new';
156import {
157  DiscreteEventPriority,
158  ContinuousEventPriority,
159  DefaultEventPriority,
160  IdleEventPriority,
161  getCurrentUpdatePriority,
162  setCurrentUpdatePriority,
163  lowerEventPriority,
164  lanesToEventPriority,
165} from './ReactEventPriorities.new';
166import {requestCurrentTransition, NoTransition} from './ReactFiberTransition';
167import {beginWork as originalBeginWork} from './ReactFiberBeginWork.new';
168import {completeWork} from './ReactFiberCompleteWork.new';
169import {unwindWork, unwindInterruptedWork} from './ReactFiberUnwindWork.new';
170import {
171  throwException,
172  createRootErrorUpdate,
173  createClassErrorUpdate,
174} from './ReactFiberThrow.new';
175import {
176  commitBeforeMutationEffects,
177  commitLayoutEffects,
178  commitMutationEffects,
179  commitPassiveEffectDurations,
180  commitPassiveMountEffects,
181  commitPassiveUnmountEffects,
182  invokeLayoutEffectMountInDEV,
183  invokePassiveEffectMountInDEV,
184  invokeLayoutEffectUnmountInDEV,
185  invokePassiveEffectUnmountInDEV,
186} from './ReactFiberCommitWork.new';
187import {enqueueUpdate} from './ReactUpdateQueue.new';
188import {resetContextDependencies} from './ReactFiberNewContext.new';
189import {
190  resetHooksAfterThrow,
191  ContextOnlyDispatcher,
192  getIsUpdatingOpaqueValueInRenderPhaseInDEV,
193} from './ReactFiberHooks.new';
194import {createCapturedValue} from './ReactCapturedValue';
195import {
196  push as pushToStack,
197  pop as popFromStack,
198  createCursor,
199} from './ReactFiberStack.new';
200import {enqueueInterleavedUpdates} from './ReactFiberInterleavedUpdates.new';
201
202import {
203  markNestedUpdateScheduled,
204  recordCommitTime,
205  resetNestedUpdateFlag,
206  startProfilerTimer,
207  stopProfilerTimerIfRunningAndRecordDelta,
208  syncNestedUpdateFlag,
209} from './ReactProfilerTimer.new';
210
211// DEV stuff
212import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
213import ReactStrictModeWarnings from './ReactStrictModeWarnings.new';
214import {
215  isRendering as ReactCurrentDebugFiberIsRenderingInDEV,
216  current as ReactCurrentFiberCurrent,
217  resetCurrentFiber as resetCurrentDebugFiberInDEV,
218  setCurrentFiber as setCurrentDebugFiberInDEV,
219} from './ReactCurrentFiber';
220import {
221  invokeGuardedCallback,
222  hasCaughtError,
223  clearCaughtError,
224} from 'shared/ReactErrorUtils';
225import {
226  onCommitRoot as onCommitRootDevTools,
227  onPostCommitRoot as onPostCommitRootDevTools,
228  isDevToolsPresent,
229} from './ReactFiberDevToolsHook.new';
230import {onCommitRoot as onCommitRootTestSelector} from './ReactTestSelectors';
231
232const ceil = Math.ceil;
233
234const {
235  ReactCurrentDispatcher,
236  ReactCurrentOwner,
237  ReactCurrentBatchConfig,
238  ReactCurrentActQueue,
239} = ReactSharedInternals;
240
241type ExecutionContext = number;
242
243export const NoContext = /*             */ 0b0000;
244const BatchedContext = /*               */ 0b0001;
245const RenderContext = /*                */ 0b0010;
246const CommitContext = /*                */ 0b0100;
247export const RetryAfterError = /*       */ 0b1000;
248
249type RootExitStatus = 0 | 1 | 2 | 3 | 4 | 5;
250const RootIncomplete = 0;
251const RootFatalErrored = 1;
252const RootErrored = 2;
253const RootSuspended = 3;
254const RootSuspendedWithDelay = 4;
255const RootCompleted = 5;
256
257// Describes where we are in the React execution stack
258let executionContext: ExecutionContext = NoContext;
259// The root we're working on
260let workInProgressRoot: FiberRoot | null = null;
261// The fiber we're working on
262let workInProgress: Fiber | null = null;
263// The lanes we're rendering
264let workInProgressRootRenderLanes: Lanes = NoLanes;
265
266// Stack that allows components to change the render lanes for its subtree
267// This is a superset of the lanes we started working on at the root. The only
268// case where it's different from `workInProgressRootRenderLanes` is when we
269// enter a subtree that is hidden and needs to be unhidden: Suspense and
270// Offscreen component.
271//
272// Most things in the work loop should deal with workInProgressRootRenderLanes.
273// Most things in begin/complete phases should deal with subtreeRenderLanes.
274export let subtreeRenderLanes: Lanes = NoLanes;
275const subtreeRenderLanesCursor: StackCursor<Lanes> = createCursor(NoLanes);
276
277// Whether to root completed, errored, suspended, etc.
278let workInProgressRootExitStatus: RootExitStatus = RootIncomplete;
279// A fatal error, if one is thrown
280let workInProgressRootFatalError: mixed = null;
281// "Included" lanes refer to lanes that were worked on during this render. It's
282// slightly different than `renderLanes` because `renderLanes` can change as you
283// enter and exit an Offscreen tree. This value is the combination of all render
284// lanes for the entire render phase.
285let workInProgressRootIncludedLanes: Lanes = NoLanes;
286// The work left over by components that were visited during this render. Only
287// includes unprocessed updates, not work in bailed out children.
288let workInProgressRootSkippedLanes: Lanes = NoLanes;
289// Lanes that were updated (in an interleaved event) during this render.
290let workInProgressRootUpdatedLanes: Lanes = NoLanes;
291// Lanes that were pinged (in an interleaved event) during this render.
292let workInProgressRootPingedLanes: Lanes = NoLanes;
293
294// The most recent time we committed a fallback. This lets us ensure a train
295// model where we don't commit new loading states in too quick succession.
296let globalMostRecentFallbackTime: number = 0;
297const FALLBACK_THROTTLE_MS: number = 500;
298
299// The absolute time for when we should start giving up on rendering
300// more and prefer CPU suspense heuristics instead.
301let workInProgressRootRenderTargetTime: number = Infinity;
302// How long a render is supposed to take before we start following CPU
303// suspense heuristics and opt out of rendering more content.
304const RENDER_TIMEOUT_MS = 500;
305
306function resetRenderTimer() {
307  workInProgressRootRenderTargetTime = now() + RENDER_TIMEOUT_MS;
308}
309
310export function getRenderTargetTime(): number {
311  return workInProgressRootRenderTargetTime;
312}
313
314let hasUncaughtError = false;
315let firstUncaughtError = null;
316let legacyErrorBoundariesThatAlreadyFailed: Set<mixed> | null = null;
317
318// Only used when enableProfilerNestedUpdateScheduledHook is true;
319// to track which root is currently committing layout effects.
320let rootCommittingMutationOrLayoutEffects: FiberRoot | null = null;
321
322let rootDoesHavePassiveEffects: boolean = false;
323let rootWithPendingPassiveEffects: FiberRoot | null = null;
324let pendingPassiveEffectsLanes: Lanes = NoLanes;
325let pendingPassiveProfilerEffects: Array<Fiber> = [];
326
327// Use these to prevent an infinite loop of nested updates
328const NESTED_UPDATE_LIMIT = 50;
329let nestedUpdateCount: number = 0;
330let rootWithNestedUpdates: FiberRoot | null = null;
331
332const NESTED_PASSIVE_UPDATE_LIMIT = 50;
333let nestedPassiveUpdateCount: number = 0;
334
335// If two updates are scheduled within the same event, we should treat their
336// event times as simultaneous, even if the actual clock time has advanced
337// between the first and second call.
338let currentEventTime: number = NoTimestamp;
339let currentEventTransitionLane: Lanes = NoLanes;
340
341export function getWorkInProgressRoot(): FiberRoot | null {
342  return workInProgressRoot;
343}
344
345export function requestEventTime() {
346  if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
347    // We're inside React, so it's fine to read the actual time.
348    return now();
349  }
350  // We're not inside React, so we may be in the middle of a browser event.
351  if (currentEventTime !== NoTimestamp) {
352    // Use the same start time for all updates until we enter React again.
353    return currentEventTime;
354  }
355  // This is the first update since React yielded. Compute a new start time.
356  currentEventTime = now();
357  return currentEventTime;
358}
359
360export function getCurrentTime() {
361  return now();
362}
363
364export function requestUpdateLane(fiber: Fiber): Lane {
365  // Special cases
366  const mode = fiber.mode;
367  if ((mode & ConcurrentMode) === NoMode) {
368    return (SyncLane: Lane);
369  } else if (
370    !deferRenderPhaseUpdateToNextBatch &&
371    (executionContext & RenderContext) !== NoContext &&
372    workInProgressRootRenderLanes !== NoLanes
373  ) {
374    // This is a render phase update. These are not officially supported. The
375    // old behavior is to give this the same "thread" (lanes) as
376    // whatever is currently rendering. So if you call `setState` on a component
377    // that happens later in the same render, it will flush. Ideally, we want to
378    // remove the special case and treat them as if they came from an
379    // interleaved event. Regardless, this pattern is not officially supported.
380    // This behavior is only a fallback. The flag only exists until we can roll
381    // out the setState warning, since existing code might accidentally rely on
382    // the current behavior.
383    return pickArbitraryLane(workInProgressRootRenderLanes);
384  }
385
386  const isTransition = requestCurrentTransition() !== NoTransition;
387  if (isTransition) {
388    // The algorithm for assigning an update to a lane should be stable for all
389    // updates at the same priority within the same event. To do this, the
390    // inputs to the algorithm must be the same.
391    //
392    // The trick we use is to cache the first of each of these inputs within an
393    // event. Then reset the cached values once we can be sure the event is
394    // over. Our heuristic for that is whenever we enter a concurrent work loop.
395    if (currentEventTransitionLane === NoLane) {
396      // All transitions within the same event are assigned the same lane.
397      currentEventTransitionLane = claimNextTransitionLane();
398    }
399    return currentEventTransitionLane;
400  }
401
402  // Updates originating inside certain React methods, like flushSync, have
403  // their priority set by tracking it with a context variable.
404  //
405  // The opaque type returned by the host config is internally a lane, so we can
406  // use that directly.
407  // TODO: Move this type conversion to the event priority module.
408  const updateLane: Lane = (getCurrentUpdatePriority(): any);
409  if (updateLane !== NoLane) {
410    return updateLane;
411  }
412
413  // This update originated outside React. Ask the host environment for an
414  // appropriate priority, based on the type of event.
415  //
416  // The opaque type returned by the host config is internally a lane, so we can
417  // use that directly.
418  // TODO: Move this type conversion to the event priority module.
419  const eventLane: Lane = (getCurrentEventPriority(): any);
420  return eventLane;
421}
422
423function requestRetryLane(fiber: Fiber) {
424  // This is a fork of `requestUpdateLane` designed specifically for Suspense
425  // "retries" — a special update that attempts to flip a Suspense boundary
426  // from its placeholder state to its primary/resolved state.
427
428  // Special cases
429  const mode = fiber.mode;
430  if ((mode & ConcurrentMode) === NoMode) {
431    return (SyncLane: Lane);
432  }
433
434  return claimNextRetryLane();
435}
436
437export function scheduleUpdateOnFiber(
438  fiber: Fiber,
439  lane: Lane,
440  eventTime: number,
441): FiberRoot | null {
442  checkForNestedUpdates();
443  warnAboutRenderPhaseUpdatesInDEV(fiber);
444
445  const root = markUpdateLaneFromFiberToRoot(fiber, lane);
446  if (root === null) {
447    return null;
448  }
449
450  if (enableUpdaterTracking) {
451    if (isDevToolsPresent) {
452      addFiberToLanesMap(root, fiber, lane);
453    }
454  }
455
456  // Mark that the root has a pending update.
457  markRootUpdated(root, lane, eventTime);
458
459  if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
460    if (
461      (executionContext & CommitContext) !== NoContext &&
462      root === rootCommittingMutationOrLayoutEffects
463    ) {
464      if (fiber.mode & ProfileMode) {
465        let current = fiber;
466        while (current !== null) {
467          if (current.tag === Profiler) {
468            const {id, onNestedUpdateScheduled} = current.memoizedProps;
469            if (typeof onNestedUpdateScheduled === 'function') {
470              onNestedUpdateScheduled(id);
471            }
472          }
473          current = current.return;
474        }
475      }
476    }
477  }
478
479  // TODO: Consolidate with `isInterleavedUpdate` check
480  if (root === workInProgressRoot) {
481    // Received an update to a tree that's in the middle of rendering. Mark
482    // that there was an interleaved update work on this root. Unless the
483    // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render
484    // phase update. In that case, we don't treat render phase updates as if
485    // they were interleaved, for backwards compat reasons.
486    if (
487      deferRenderPhaseUpdateToNextBatch ||
488      (executionContext & RenderContext) === NoContext
489    ) {
490      workInProgressRootUpdatedLanes = mergeLanes(
491        workInProgressRootUpdatedLanes,
492        lane,
493      );
494    }
495    if (workInProgressRootExitStatus === RootSuspendedWithDelay) {
496      // The root already suspended with a delay, which means this render
497      // definitely won't finish. Since we have a new update, let's mark it as
498      // suspended now, right before marking the incoming update. This has the
499      // effect of interrupting the current render and switching to the update.
500      // TODO: Make sure this doesn't override pings that happen while we've
501      // already started rendering.
502      markRootSuspended(root, workInProgressRootRenderLanes);
503    }
504  }
505
506  ensureRootIsScheduled(root, eventTime);
507  if (
508    lane === SyncLane &&
509    executionContext === NoContext &&
510    (fiber.mode & ConcurrentMode) === NoMode &&
511    // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.
512    !(__DEV__ && ReactCurrentActQueue.isBatchingLegacy)
513  ) {
514    // Flush the synchronous work now, unless we're already working or inside
515    // a batch. This is intentionally inside scheduleUpdateOnFiber instead of
516    // scheduleCallbackForFiber to preserve the ability to schedule a callback
517    // without immediately flushing it. We only do this for user-initiated
518    // updates, to preserve historical behavior of legacy mode.
519    resetRenderTimer();
520    flushSyncCallbacksOnlyInLegacyMode();
521  }
522
523  return root;
524}
525
526// This is split into a separate function so we can mark a fiber with pending
527// work without treating it as a typical update that originates from an event;
528// e.g. retrying a Suspense boundary isn't an update, but it does schedule work
529// on a fiber.
530function markUpdateLaneFromFiberToRoot(
531  sourceFiber: Fiber,
532  lane: Lane,
533): FiberRoot | null {
534  // Update the source fiber's lanes
535  sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
536  let alternate = sourceFiber.alternate;
537  if (alternate !== null) {
538    alternate.lanes = mergeLanes(alternate.lanes, lane);
539  }
540  if (__DEV__) {
541    if (
542      alternate === null &&
543      (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags
544    ) {
545      warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
546    }
547  }
548  // Walk the parent path to the root and update the child lanes.
549  let node = sourceFiber;
550  let parent = sourceFiber.return;
551  while (parent !== null) {
552    parent.childLanes = mergeLanes(parent.childLanes, lane);
553    alternate = parent.alternate;
554    if (alternate !== null) {
555      alternate.childLanes = mergeLanes(alternate.childLanes, lane);
556    } else {
557      if (__DEV__) {
558        if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {
559          warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
560        }
561      }
562    }
563    node = parent;
564    parent = parent.return;
565  }
566  if (node.tag === HostRoot) {
567    const root: FiberRoot = node.stateNode;
568    return root;
569  } else {
570    return null;
571  }
572}
573
574export function isInterleavedUpdate(fiber: Fiber, lane: Lane) {
575  return (
576    // TODO: Optimize slightly by comparing to root that fiber belongs to.
577    // Requires some refactoring. Not a big deal though since it's rare for
578    // concurrent apps to have more than a single root.
579    workInProgressRoot !== null &&
580    (fiber.mode & ConcurrentMode) !== NoMode &&
581    // If this is a render phase update (i.e. UNSAFE_componentWillReceiveProps),
582    // then don't treat this as an interleaved update. This pattern is
583    // accompanied by a warning but we haven't fully deprecated it yet. We can
584    // remove once the deferRenderPhaseUpdateToNextBatch flag is enabled.
585    (deferRenderPhaseUpdateToNextBatch ||
586      (executionContext & RenderContext) === NoContext)
587  );
588}
589
590// Use this function to schedule a task for a root. There's only one task per
591// root; if a task was already scheduled, we'll check to make sure the priority
592// of the existing task is the same as the priority of the next level that the
593// root has work on. This function is called on every update, and right before
594// exiting a task.
595function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
596  const existingCallbackNode = root.callbackNode;
597
598  // Check if any lanes are being starved by other work. If so, mark them as
599  // expired so we know to work on those next.
600  markStarvedLanesAsExpired(root, currentTime);
601
602  // Determine the next lanes to work on, and their priority.
603  const nextLanes = getNextLanes(
604    root,
605    root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
606  );
607
608  if (nextLanes === NoLanes) {
609    // Special case: There's nothing to work on.
610    if (existingCallbackNode !== null) {
611      cancelCallback(existingCallbackNode);
612    }
613    root.callbackNode = null;
614    root.callbackPriority = NoLane;
615    return;
616  }
617
618  // We use the highest priority lane to represent the priority of the callback.
619  const newCallbackPriority = getHighestPriorityLane(nextLanes);
620
621  // Check if there's an existing task. We may be able to reuse it.
622  const existingCallbackPriority = root.callbackPriority;
623  if (
624    existingCallbackPriority === newCallbackPriority &&
625    // Special case related to `act`. If the currently scheduled task is a
626    // Scheduler task, rather than an `act` task, cancel it and re-scheduled
627    // on the `act` queue.
628    !(
629      __DEV__ &&
630      ReactCurrentActQueue.current !== null &&
631      existingCallbackNode !== fakeActCallbackNode
632    )
633  ) {
634    if (__DEV__) {
635      // If we're going to re-use an existing task, it needs to exist.
636      // Assume that discrete update microtasks are non-cancellable and null.
637      // TODO: Temporary until we confirm this warning is not fired.
638      if (
639        existingCallbackNode == null &&
640        existingCallbackPriority !== SyncLane
641      ) {
642        console.error(
643          'Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue.',
644        );
645      }
646    }
647    // The priority hasn't changed. We can reuse the existing task. Exit.
648    return;
649  }
650
651  if (existingCallbackNode != null) {
652    // Cancel the existing callback. We'll schedule a new one below.
653    cancelCallback(existingCallbackNode);
654  }
655
656  // Schedule a new callback.
657  let newCallbackNode;
658  if (newCallbackPriority === SyncLane) {
659    // Special case: Sync React callbacks are scheduled on a special
660    // internal queue
661    if (root.tag === LegacyRoot) {
662      if (__DEV__ && ReactCurrentActQueue.isBatchingLegacy !== null) {
663        ReactCurrentActQueue.didScheduleLegacyUpdate = true;
664      }
665      scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));
666    } else {
667      scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
668    }
669    if (supportsMicrotasks) {
670      // Flush the queue in a microtask.
671      if (__DEV__ && ReactCurrentActQueue.current !== null) {
672        // Inside `act`, use our internal `act` queue so that these get flushed
673        // at the end of the current scope even when using the sync version
674        // of `act`.
675        ReactCurrentActQueue.current.push(flushSyncCallbacks);
676      } else {
677        scheduleMicrotask(flushSyncCallbacks);
678      }
679    } else {
680      // Flush the queue in an Immediate task.
681      scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks);
682    }
683    newCallbackNode = null;
684  } else {
685    let schedulerPriorityLevel;
686    switch (lanesToEventPriority(nextLanes)) {
687      case DiscreteEventPriority:
688        schedulerPriorityLevel = ImmediateSchedulerPriority;
689        break;
690      case ContinuousEventPriority:
691        schedulerPriorityLevel = UserBlockingSchedulerPriority;
692        break;
693      case DefaultEventPriority:
694        schedulerPriorityLevel = NormalSchedulerPriority;
695        break;
696      case IdleEventPriority:
697        schedulerPriorityLevel = IdleSchedulerPriority;
698        break;
699      default:
700        schedulerPriorityLevel = NormalSchedulerPriority;
701        break;
702    }
703    newCallbackNode = scheduleCallback(
704      schedulerPriorityLevel,
705      performConcurrentWorkOnRoot.bind(null, root),
706    );
707  }
708
709  root.callbackPriority = newCallbackPriority;
710  root.callbackNode = newCallbackNode;
711}
712
713// This is the entry point for every concurrent task, i.e. anything that
714// goes through Scheduler.
715function performConcurrentWorkOnRoot(root, didTimeout) {
716  if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {
717    resetNestedUpdateFlag();
718  }
719
720  // Since we know we're in a React event, we can clear the current
721  // event time. The next update will compute a new event time.
722  currentEventTime = NoTimestamp;
723  currentEventTransitionLane = NoLanes;
724
725  invariant(
726    (executionContext & (RenderContext | CommitContext)) === NoContext,
727    'Should not already be working.',
728  );
729
730  // Flush any pending passive effects before deciding which lanes to work on,
731  // in case they schedule additional work.
732  const originalCallbackNode = root.callbackNode;
733  const didFlushPassiveEffects = flushPassiveEffects();
734  if (didFlushPassiveEffects) {
735    // Something in the passive effect phase may have canceled the current task.
736    // Check if the task node for this root was changed.
737    if (root.callbackNode !== originalCallbackNode) {
738      // The current task was canceled. Exit. We don't need to call
739      // `ensureRootIsScheduled` because the check above implies either that
740      // there's a new task, or that there's no remaining work on this root.
741      return null;
742    } else {
743      // Current task was not canceled. Continue.
744    }
745  }
746
747  // Determine the next lanes to work on, using the fields stored
748  // on the root.
749  let lanes = getNextLanes(
750    root,
751    root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
752  );
753  if (lanes === NoLanes) {
754    // Defensive coding. This is never expected to happen.
755    return null;
756  }
757
758  // We disable time-slicing in some cases: if the work has been CPU-bound
759  // for too long ("expired" work, to prevent starvation), or we're in
760  // sync-updates-by-default mode.
761  // TODO: We only check `didTimeout` defensively, to account for a Scheduler
762  // bug we're still investigating. Once the bug in Scheduler is fixed,
763  // we can remove this, since we track expiration ourselves.
764  let exitStatus =
765    shouldTimeSlice(root, lanes) &&
766    (disableSchedulerTimeoutInWorkLoop || !didTimeout)
767      ? renderRootConcurrent(root, lanes)
768      : renderRootSync(root, lanes);
769  if (exitStatus !== RootIncomplete) {
770    if (exitStatus === RootErrored) {
771      const prevExecutionContext = executionContext;
772      executionContext |= RetryAfterError;
773
774      // If an error occurred during hydration,
775      // discard server response and fall back to client side render.
776      if (root.hydrate) {
777        root.hydrate = false;
778        if (__DEV__) {
779          errorHydratingContainer(root.containerInfo);
780        }
781        clearContainer(root.containerInfo);
782      }
783
784      // If something threw an error, try rendering one more time. We'll render
785      // synchronously to block concurrent data mutations, and we'll includes
786      // all pending updates are included. If it still fails after the second
787      // attempt, we'll give up and commit the resulting tree.
788      const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);
789      if (errorRetryLanes !== NoLanes) {
790        lanes = errorRetryLanes;
791        exitStatus = renderRootSync(root, errorRetryLanes);
792      }
793
794      executionContext = prevExecutionContext;
795    }
796
797    if (exitStatus === RootFatalErrored) {
798      const fatalError = workInProgressRootFatalError;
799      prepareFreshStack(root, NoLanes);
800      markRootSuspended(root, lanes);
801      ensureRootIsScheduled(root, now());
802      throw fatalError;
803    }
804
805    // We now have a consistent tree. The next step is either to commit it,
806    // or, if something suspended, wait to commit it after a timeout.
807    const finishedWork: Fiber = (root.current.alternate: any);
808    root.finishedWork = finishedWork;
809    root.finishedLanes = lanes;
810    finishConcurrentRender(root, exitStatus, lanes);
811  }
812
813  ensureRootIsScheduled(root, now());
814  if (root.callbackNode === originalCallbackNode) {
815    // The task node scheduled for this root is the same one that's
816    // currently executed. Need to return a continuation.
817    return performConcurrentWorkOnRoot.bind(null, root);
818  }
819  return null;
820}
821
822function finishConcurrentRender(root, exitStatus, lanes) {
823  switch (exitStatus) {
824    case RootIncomplete:
825    case RootFatalErrored: {
826      invariant(false, 'Root did not complete. This is a bug in React.');
827    }
828    // Flow knows about invariant, so it complains if I add a break
829    // statement, but eslint doesn't know about invariant, so it complains
830    // if I do. eslint-disable-next-line no-fallthrough
831    case RootErrored: {
832      // We should have already attempted to retry this tree. If we reached
833      // this point, it errored again. Commit it.
834      commitRoot(root);
835      break;
836    }
837    case RootSuspended: {
838      markRootSuspended(root, lanes);
839
840      // We have an acceptable loading state. We need to figure out if we
841      // should immediately commit it or wait a bit.
842
843      if (
844        includesOnlyRetries(lanes) &&
845        // do not delay if we're inside an act() scope
846        !shouldForceFlushFallbacksInDEV()
847      ) {
848        // This render only included retries, no updates. Throttle committing
849        // retries so that we don't show too many loading states too quickly.
850        const msUntilTimeout =
851          globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();
852        // Don't bother with a very short suspense time.
853        if (msUntilTimeout > 10) {
854          const nextLanes = getNextLanes(root, NoLanes);
855          if (nextLanes !== NoLanes) {
856            // There's additional work on this root.
857            break;
858          }
859          const suspendedLanes = root.suspendedLanes;
860          if (!isSubsetOfLanes(suspendedLanes, lanes)) {
861            // We should prefer to render the fallback of at the last
862            // suspended level. Ping the last suspended level to try
863            // rendering it again.
864            // FIXME: What if the suspended lanes are Idle? Should not restart.
865            const eventTime = requestEventTime();
866            markRootPinged(root, suspendedLanes, eventTime);
867            break;
868          }
869
870          // The render is suspended, it hasn't timed out, and there's no
871          // lower priority work to do. Instead of committing the fallback
872          // immediately, wait for more data to arrive.
873          root.timeoutHandle = scheduleTimeout(
874            commitRoot.bind(null, root),
875            msUntilTimeout,
876          );
877          break;
878        }
879      }
880      // The work expired. Commit immediately.
881      commitRoot(root);
882      break;
883    }
884    case RootSuspendedWithDelay: {
885      markRootSuspended(root, lanes);
886
887      if (includesOnlyTransitions(lanes)) {
888        // This is a transition, so we should exit without committing a
889        // placeholder and without scheduling a timeout. Delay indefinitely
890        // until we receive more data.
891        break;
892      }
893
894      if (!shouldForceFlushFallbacksInDEV()) {
895        // This is not a transition, but we did trigger an avoided state.
896        // Schedule a placeholder to display after a short delay, using the Just
897        // Noticeable Difference.
898        // TODO: Is the JND optimization worth the added complexity? If this is
899        // the only reason we track the event time, then probably not.
900        // Consider removing.
901
902        const mostRecentEventTime = getMostRecentEventTime(root, lanes);
903        const eventTimeMs = mostRecentEventTime;
904        const timeElapsedMs = now() - eventTimeMs;
905        const msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs;
906
907        // Don't bother with a very short suspense time.
908        if (msUntilTimeout > 10) {
909          // Instead of committing the fallback immediately, wait for more data
910          // to arrive.
911          root.timeoutHandle = scheduleTimeout(
912            commitRoot.bind(null, root),
913            msUntilTimeout,
914          );
915          break;
916        }
917      }
918
919      // Commit the placeholder.
920      commitRoot(root);
921      break;
922    }
923    case RootCompleted: {
924      // The work completed. Ready to commit.
925      commitRoot(root);
926      break;
927    }
928    default: {
929      invariant(false, 'Unknown root exit status.');
930    }
931  }
932}
933
934function markRootSuspended(root, suspendedLanes) {
935  // When suspending, we should always exclude lanes that were pinged or (more
936  // rarely, since we try to avoid it) updated during the render phase.
937  // TODO: Lol maybe there's a better way to factor this besides this
938  // obnoxiously named function :)
939  suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);
940  suspendedLanes = removeLanes(suspendedLanes, workInProgressRootUpdatedLanes);
941  markRootSuspended_dontCallThisOneDirectly(root, suspendedLanes);
942}
943
944// This is the entry point for synchronous tasks that don't go
945// through Scheduler
946function performSyncWorkOnRoot(root) {
947  if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {
948    syncNestedUpdateFlag();
949  }
950
951  invariant(
952    (executionContext & (RenderContext | CommitContext)) === NoContext,
953    'Should not already be working.',
954  );
955
956  flushPassiveEffects();
957
958  let lanes = getNextLanes(root, NoLanes);
959  if (!includesSomeLane(lanes, SyncLane)) {
960    // There's no remaining sync work left.
961    ensureRootIsScheduled(root, now());
962    return null;
963  }
964
965  let exitStatus = renderRootSync(root, lanes);
966  if (root.tag !== LegacyRoot && exitStatus === RootErrored) {
967    const prevExecutionContext = executionContext;
968    executionContext |= RetryAfterError;
969
970    // If an error occurred during hydration,
971    // discard server response and fall back to client side render.
972    if (root.hydrate) {
973      root.hydrate = false;
974      if (__DEV__) {
975        errorHydratingContainer(root.containerInfo);
976      }
977      clearContainer(root.containerInfo);
978    }
979
980    // If something threw an error, try rendering one more time. We'll render
981    // synchronously to block concurrent data mutations, and we'll includes
982    // all pending updates are included. If it still fails after the second
983    // attempt, we'll give up and commit the resulting tree.
984    const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);
985    if (errorRetryLanes !== NoLanes) {
986      lanes = errorRetryLanes;
987      exitStatus = renderRootSync(root, lanes);
988    }
989
990    executionContext = prevExecutionContext;
991  }
992
993  if (exitStatus === RootFatalErrored) {
994    const fatalError = workInProgressRootFatalError;
995    prepareFreshStack(root, NoLanes);
996    markRootSuspended(root, lanes);
997    ensureRootIsScheduled(root, now());
998    throw fatalError;
999  }
1000
1001  // We now have a consistent tree. Because this is a sync render, we
1002  // will commit it even if something suspended.
1003  const finishedWork: Fiber = (root.current.alternate: any);
1004  root.finishedWork = finishedWork;
1005  root.finishedLanes = lanes;
1006  commitRoot(root);
1007
1008  // Before exiting, make sure there's a callback scheduled for the next
1009  // pending level.
1010  ensureRootIsScheduled(root, now());
1011
1012  return null;
1013}
1014
1015export function flushRoot(root: FiberRoot, lanes: Lanes) {
1016  if (lanes !== NoLanes) {
1017    markRootEntangled(root, mergeLanes(lanes, SyncLane));
1018    ensureRootIsScheduled(root, now());
1019    if ((executionContext & (RenderContext | CommitContext)) === NoContext) {
1020      resetRenderTimer();
1021      flushSyncCallbacks();
1022    }
1023  }
1024}
1025
1026export function getExecutionContext(): ExecutionContext {
1027  return executionContext;
1028}
1029
1030export function deferredUpdates<A>(fn: () => A): A {
1031  const previousPriority = getCurrentUpdatePriority();
1032  const prevTransition = ReactCurrentBatchConfig.transition;
1033  try {
1034    ReactCurrentBatchConfig.transition = 0;
1035    setCurrentUpdatePriority(DefaultEventPriority);
1036    return fn();
1037  } finally {
1038    setCurrentUpdatePriority(previousPriority);
1039    ReactCurrentBatchConfig.transition = prevTransition;
1040  }
1041}
1042
1043export function batchedUpdates<A, R>(fn: A => R, a: A): R {
1044  const prevExecutionContext = executionContext;
1045  executionContext |= BatchedContext;
1046  try {
1047    return fn(a);
1048  } finally {
1049    executionContext = prevExecutionContext;
1050    // If there were legacy sync updates, flush them at the end of the outer
1051    // most batchedUpdates-like method.
1052    if (
1053      executionContext === NoContext &&
1054      // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.
1055      !(__DEV__ && ReactCurrentActQueue.isBatchingLegacy)
1056    ) {
1057      resetRenderTimer();
1058      flushSyncCallbacksOnlyInLegacyMode();
1059    }
1060  }
1061}
1062
1063export function discreteUpdates<A, B, C, D, R>(
1064  fn: (A, B, C, D) => R,
1065  a: A,
1066  b: B,
1067  c: C,
1068  d: D,
1069): R {
1070  const previousPriority = getCurrentUpdatePriority();
1071  const prevTransition = ReactCurrentBatchConfig.transition;
1072  try {
1073    ReactCurrentBatchConfig.transition = 0;
1074    setCurrentUpdatePriority(DiscreteEventPriority);
1075    return fn(a, b, c, d);
1076  } finally {
1077    setCurrentUpdatePriority(previousPriority);
1078    ReactCurrentBatchConfig.transition = prevTransition;
1079    if (executionContext === NoContext) {
1080      resetRenderTimer();
1081    }
1082  }
1083}
1084
1085// Overload the definition to the two valid signatures.
1086// Warning, this opts-out of checking the function body.
1087declare function flushSyncWithoutWarningIfAlreadyRendering<R>(fn: () => R): R;
1088// eslint-disable-next-line no-redeclare
1089declare function flushSyncWithoutWarningIfAlreadyRendering(): void;
1090// eslint-disable-next-line no-redeclare
1091export function flushSyncWithoutWarningIfAlreadyRendering(fn) {
1092  // In legacy mode, we flush pending passive effects at the beginning of the
1093  // next event, not at the end of the previous one.
1094  if (
1095    rootWithPendingPassiveEffects !== null &&
1096    rootWithPendingPassiveEffects.tag === LegacyRoot &&
1097    (executionContext & (RenderContext | CommitContext)) === NoContext
1098  ) {
1099    flushPassiveEffects();
1100  }
1101
1102  const prevExecutionContext = executionContext;
1103  executionContext |= BatchedContext;
1104
1105  const prevTransition = ReactCurrentBatchConfig.transition;
1106  const previousPriority = getCurrentUpdatePriority();
1107  try {
1108    ReactCurrentBatchConfig.transition = 0;
1109    setCurrentUpdatePriority(DiscreteEventPriority);
1110    if (fn) {
1111      return fn();
1112    } else {
1113      return undefined;
1114    }
1115  } finally {
1116    setCurrentUpdatePriority(previousPriority);
1117    ReactCurrentBatchConfig.transition = prevTransition;
1118    executionContext = prevExecutionContext;
1119    // Flush the immediate callbacks that were scheduled during this batch.
1120    // Note that this will happen even if batchedUpdates is higher up
1121    // the stack.
1122    if ((executionContext & (RenderContext | CommitContext)) === NoContext) {
1123      flushSyncCallbacks();
1124    }
1125  }
1126}
1127
1128// Overload the definition to the two valid signatures.
1129// Warning, this opts-out of checking the function body.
1130declare function flushSync<R>(fn: () => R): R;
1131// eslint-disable-next-line no-redeclare
1132declare function flushSync(): void;
1133// eslint-disable-next-line no-redeclare
1134export function flushSync(fn) {
1135  if (__DEV__) {
1136    if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
1137      console.error(
1138        'flushSync was called from inside a lifecycle method. React cannot ' +
1139          'flush when React is already rendering. Consider moving this call to ' +
1140          'a scheduler task or micro task.',
1141      );
1142    }
1143  }
1144  return flushSyncWithoutWarningIfAlreadyRendering(fn);
1145}
1146
1147export function flushControlled(fn: () => mixed): void {
1148  const prevExecutionContext = executionContext;
1149  executionContext |= BatchedContext;
1150  const prevTransition = ReactCurrentBatchConfig.transition;
1151  const previousPriority = getCurrentUpdatePriority();
1152  try {
1153    ReactCurrentBatchConfig.transition = 0;
1154    setCurrentUpdatePriority(DiscreteEventPriority);
1155    fn();
1156  } finally {
1157    setCurrentUpdatePriority(previousPriority);
1158    ReactCurrentBatchConfig.transition = prevTransition;
1159
1160    executionContext = prevExecutionContext;
1161    if (executionContext === NoContext) {
1162      // Flush the immediate callbacks that were scheduled during this batch
1163      resetRenderTimer();
1164      flushSyncCallbacks();
1165    }
1166  }
1167}
1168
1169export function pushRenderLanes(fiber: Fiber, lanes: Lanes) {
1170  pushToStack(subtreeRenderLanesCursor, subtreeRenderLanes, fiber);
1171  subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);
1172  workInProgressRootIncludedLanes = mergeLanes(
1173    workInProgressRootIncludedLanes,
1174    lanes,
1175  );
1176}
1177
1178export function popRenderLanes(fiber: Fiber) {
1179  subtreeRenderLanes = subtreeRenderLanesCursor.current;
1180  popFromStack(subtreeRenderLanesCursor, fiber);
1181}
1182
1183function prepareFreshStack(root: FiberRoot, lanes: Lanes) {
1184  root.finishedWork = null;
1185  root.finishedLanes = NoLanes;
1186
1187  const timeoutHandle = root.timeoutHandle;
1188  if (timeoutHandle !== noTimeout) {
1189    // The root previous suspended and scheduled a timeout to commit a fallback
1190    // state. Now that we have additional work, cancel the timeout.
1191    root.timeoutHandle = noTimeout;
1192    // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
1193    cancelTimeout(timeoutHandle);
1194  }
1195
1196  if (workInProgress !== null) {
1197    let interruptedWork = workInProgress.return;
1198    while (interruptedWork !== null) {
1199      unwindInterruptedWork(interruptedWork, workInProgressRootRenderLanes);
1200      interruptedWork = interruptedWork.return;
1201    }
1202  }
1203  workInProgressRoot = root;
1204  workInProgress = createWorkInProgress(root.current, null);
1205  workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;
1206  workInProgressRootExitStatus = RootIncomplete;
1207  workInProgressRootFatalError = null;
1208  workInProgressRootSkippedLanes = NoLanes;
1209  workInProgressRootUpdatedLanes = NoLanes;
1210  workInProgressRootPingedLanes = NoLanes;
1211
1212  enqueueInterleavedUpdates();
1213
1214  if (__DEV__) {
1215    ReactStrictModeWarnings.discardPendingWarnings();
1216  }
1217}
1218
1219function handleError(root, thrownValue): void {
1220  do {
1221    let erroredWork = workInProgress;
1222    try {
1223      // Reset module-level state that was set during the render phase.
1224      resetContextDependencies();
1225      resetHooksAfterThrow();
1226      resetCurrentDebugFiberInDEV();
1227      // TODO: I found and added this missing line while investigating a
1228      // separate issue. Write a regression test using string refs.
1229      ReactCurrentOwner.current = null;
1230
1231      if (erroredWork === null || erroredWork.return === null) {
1232        // Expected to be working on a non-root fiber. This is a fatal error
1233        // because there's no ancestor that can handle it; the root is
1234        // supposed to capture all errors that weren't caught by an error
1235        // boundary.
1236        workInProgressRootExitStatus = RootFatalErrored;
1237        workInProgressRootFatalError = thrownValue;
1238        // Set `workInProgress` to null. This represents advancing to the next
1239        // sibling, or the parent if there are no siblings. But since the root
1240        // has no siblings nor a parent, we set it to null. Usually this is
1241        // handled by `completeUnitOfWork` or `unwindWork`, but since we're
1242        // intentionally not calling those, we need set it here.
1243        // TODO: Consider calling `unwindWork` to pop the contexts.
1244        workInProgress = null;
1245        return;
1246      }
1247
1248      if (enableProfilerTimer && erroredWork.mode & ProfileMode) {
1249        // Record the time spent rendering before an error was thrown. This
1250        // avoids inaccurate Profiler durations in the case of a
1251        // suspended render.
1252        stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);
1253      }
1254
1255      throwException(
1256        root,
1257        erroredWork.return,
1258        erroredWork,
1259        thrownValue,
1260        workInProgressRootRenderLanes,
1261      );
1262      completeUnitOfWork(erroredWork);
1263    } catch (yetAnotherThrownValue) {
1264      // Something in the return path also threw.
1265      thrownValue = yetAnotherThrownValue;
1266      if (workInProgress === erroredWork && erroredWork !== null) {
1267        // If this boundary has already errored, then we had trouble processing
1268        // the error. Bubble it to the next boundary.
1269        erroredWork = erroredWork.return;
1270        workInProgress = erroredWork;
1271      } else {
1272        erroredWork = workInProgress;
1273      }
1274      continue;
1275    }
1276    // Return to the normal work loop.
1277    return;
1278  } while (true);
1279}
1280
1281function pushDispatcher() {
1282  const prevDispatcher = ReactCurrentDispatcher.current;
1283  ReactCurrentDispatcher.current = ContextOnlyDispatcher;
1284  if (prevDispatcher === null) {
1285    // The React isomorphic package does not include a default dispatcher.
1286    // Instead the first renderer will lazily attach one, in order to give
1287    // nicer error messages.
1288    return ContextOnlyDispatcher;
1289  } else {
1290    return prevDispatcher;
1291  }
1292}
1293
1294function popDispatcher(prevDispatcher) {
1295  ReactCurrentDispatcher.current = prevDispatcher;
1296}
1297
1298export function markCommitTimeOfFallback() {
1299  globalMostRecentFallbackTime = now();
1300}
1301
1302export function markSkippedUpdateLanes(lane: Lane | Lanes): void {
1303  workInProgressRootSkippedLanes = mergeLanes(
1304    lane,
1305    workInProgressRootSkippedLanes,
1306  );
1307}
1308
1309export function renderDidSuspend(): void {
1310  if (workInProgressRootExitStatus === RootIncomplete) {
1311    workInProgressRootExitStatus = RootSuspended;
1312  }
1313}
1314
1315export function renderDidSuspendDelayIfPossible(): void {
1316  if (
1317    workInProgressRootExitStatus === RootIncomplete ||
1318    workInProgressRootExitStatus === RootSuspended
1319  ) {
1320    workInProgressRootExitStatus = RootSuspendedWithDelay;
1321  }
1322
1323  // Check if there are updates that we skipped tree that might have unblocked
1324  // this render.
1325  if (
1326    workInProgressRoot !== null &&
1327    (includesNonIdleWork(workInProgressRootSkippedLanes) ||
1328      includesNonIdleWork(workInProgressRootUpdatedLanes))
1329  ) {
1330    // Mark the current render as suspended so that we switch to working on
1331    // the updates that were skipped. Usually we only suspend at the end of
1332    // the render phase.
1333    // TODO: We should probably always mark the root as suspended immediately
1334    // (inside this function), since by suspending at the end of the render
1335    // phase introduces a potential mistake where we suspend lanes that were
1336    // pinged or updated while we were rendering.
1337    markRootSuspended(workInProgressRoot, workInProgressRootRenderLanes);
1338  }
1339}
1340
1341export function renderDidError() {
1342  if (workInProgressRootExitStatus !== RootCompleted) {
1343    workInProgressRootExitStatus = RootErrored;
1344  }
1345}
1346
1347// Called during render to determine if anything has suspended.
1348// Returns false if we're not sure.
1349export function renderHasNotSuspendedYet(): boolean {
1350  // If something errored or completed, we can't really be sure,
1351  // so those are false.
1352  return workInProgressRootExitStatus === RootIncomplete;
1353}
1354
1355function renderRootSync(root: FiberRoot, lanes: Lanes) {
1356  const prevExecutionContext = executionContext;
1357  executionContext |= RenderContext;
1358  const prevDispatcher = pushDispatcher();
1359
1360  // If the root or lanes have changed, throw out the existing stack
1361  // and prepare a fresh one. Otherwise we'll continue where we left off.
1362  if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
1363    if (enableUpdaterTracking) {
1364      if (isDevToolsPresent) {
1365        const memoizedUpdaters = root.memoizedUpdaters;
1366        if (memoizedUpdaters.size > 0) {
1367          restorePendingUpdaters(root, workInProgressRootRenderLanes);
1368          memoizedUpdaters.clear();
1369        }
1370
1371        // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.
1372        // If we bailout on this work, we'll move them back (like above).
1373        // It's important to move them now in case the work spawns more work at the same priority with different updaters.
1374        // That way we can keep the current update and future updates separate.
1375        movePendingFibersToMemoized(root, lanes);
1376      }
1377    }
1378
1379    prepareFreshStack(root, lanes);
1380  }
1381
1382  if (__DEV__) {
1383    if (enableDebugTracing) {
1384      logRenderStarted(lanes);
1385    }
1386  }
1387
1388  if (enableSchedulingProfiler) {
1389    markRenderStarted(lanes);
1390  }
1391
1392  do {
1393    try {
1394      workLoopSync();
1395      break;
1396    } catch (thrownValue) {
1397      handleError(root, thrownValue);
1398    }
1399  } while (true);
1400  resetContextDependencies();
1401
1402  executionContext = prevExecutionContext;
1403  popDispatcher(prevDispatcher);
1404
1405  if (workInProgress !== null) {
1406    // This is a sync render, so we should have finished the whole tree.
1407    invariant(
1408      false,
1409      'Cannot commit an incomplete root. This error is likely caused by a ' +
1410        'bug in React. Please file an issue.',
1411    );
1412  }
1413
1414  if (__DEV__) {
1415    if (enableDebugTracing) {
1416      logRenderStopped();
1417    }
1418  }
1419
1420  if (enableSchedulingProfiler) {
1421    markRenderStopped();
1422  }
1423
1424  // Set this to null to indicate there's no in-progress render.
1425  workInProgressRoot = null;
1426  workInProgressRootRenderLanes = NoLanes;
1427
1428  return workInProgressRootExitStatus;
1429}
1430
1431// The work loop is an extremely hot path. Tell Closure not to inline it.
1432/** @noinline */
1433function workLoopSync() {
1434  // Already timed out, so perform work without checking if we need to yield.
1435  while (workInProgress !== null) {
1436    performUnitOfWork(workInProgress);
1437  }
1438}
1439
1440function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
1441  const prevExecutionContext = executionContext;
1442  executionContext |= RenderContext;
1443  const prevDispatcher = pushDispatcher();
1444
1445  // If the root or lanes have changed, throw out the existing stack
1446  // and prepare a fresh one. Otherwise we'll continue where we left off.
1447  if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
1448    if (enableUpdaterTracking) {
1449      if (isDevToolsPresent) {
1450        const memoizedUpdaters = root.memoizedUpdaters;
1451        if (memoizedUpdaters.size > 0) {
1452          restorePendingUpdaters(root, workInProgressRootRenderLanes);
1453          memoizedUpdaters.clear();
1454        }
1455
1456        // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.
1457        // If we bailout on this work, we'll move them back (like above).
1458        // It's important to move them now in case the work spawns more work at the same priority with different updaters.
1459        // That way we can keep the current update and future updates separate.
1460        movePendingFibersToMemoized(root, lanes);
1461      }
1462    }
1463
1464    resetRenderTimer();
1465    prepareFreshStack(root, lanes);
1466  }
1467
1468  if (__DEV__) {
1469    if (enableDebugTracing) {
1470      logRenderStarted(lanes);
1471    }
1472  }
1473
1474  if (enableSchedulingProfiler) {
1475    markRenderStarted(lanes);
1476  }
1477
1478  do {
1479    try {
1480      workLoopConcurrent();
1481      break;
1482    } catch (thrownValue) {
1483      handleError(root, thrownValue);
1484    }
1485  } while (true);
1486  resetContextDependencies();
1487
1488  popDispatcher(prevDispatcher);
1489  executionContext = prevExecutionContext;
1490
1491  if (__DEV__) {
1492    if (enableDebugTracing) {
1493      logRenderStopped();
1494    }
1495  }
1496
1497  // Check if the tree has completed.
1498  if (workInProgress !== null) {
1499    // Still work remaining.
1500    if (enableSchedulingProfiler) {
1501      markRenderYielded();
1502    }
1503    return RootIncomplete;
1504  } else {
1505    // Completed the tree.
1506    if (enableSchedulingProfiler) {
1507      markRenderStopped();
1508    }
1509
1510    // Set this to null to indicate there's no in-progress render.
1511    workInProgressRoot = null;
1512    workInProgressRootRenderLanes = NoLanes;
1513
1514    // Return the final exit status.
1515    return workInProgressRootExitStatus;
1516  }
1517}
1518
1519/** @noinline */
1520function workLoopConcurrent() {
1521  // Perform work until Scheduler asks us to yield
1522  while (workInProgress !== null && !shouldYield()) {
1523    performUnitOfWork(workInProgress);
1524  }
1525}
1526
1527function performUnitOfWork(unitOfWork: Fiber): void {
1528  // The current, flushed, state of this fiber is the alternate. Ideally
1529  // nothing should rely on this, but relying on it here means that we don't
1530  // need an additional field on the work in progress.
1531  const current = unitOfWork.alternate;
1532  setCurrentDebugFiberInDEV(unitOfWork);
1533
1534  let next;
1535  if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {
1536    startProfilerTimer(unitOfWork);
1537    next = beginWork(current, unitOfWork, subtreeRenderLanes);
1538    stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
1539  } else {
1540    next = beginWork(current, unitOfWork, subtreeRenderLanes);
1541  }
1542
1543  resetCurrentDebugFiberInDEV();
1544  unitOfWork.memoizedProps = unitOfWork.pendingProps;
1545  if (next === null) {
1546    // If this doesn't spawn new work, complete the current work.
1547    completeUnitOfWork(unitOfWork);
1548  } else {
1549    workInProgress = next;
1550  }
1551
1552  ReactCurrentOwner.current = null;
1553}
1554
1555function completeUnitOfWork(unitOfWork: Fiber): void {
1556  // Attempt to complete the current unit of work, then move to the next
1557  // sibling. If there are no more siblings, return to the parent fiber.
1558  let completedWork = unitOfWork;
1559  do {
1560    // The current, flushed, state of this fiber is the alternate. Ideally
1561    // nothing should rely on this, but relying on it here means that we don't
1562    // need an additional field on the work in progress.
1563    const current = completedWork.alternate;
1564    const returnFiber = completedWork.return;
1565
1566    // Check if the work completed or if something threw.
1567    if ((completedWork.flags & Incomplete) === NoFlags) {
1568      setCurrentDebugFiberInDEV(completedWork);
1569      let next;
1570      if (
1571        !enableProfilerTimer ||
1572        (completedWork.mode & ProfileMode) === NoMode
1573      ) {
1574        next = completeWork(current, completedWork, subtreeRenderLanes);
1575      } else {
1576        startProfilerTimer(completedWork);
1577        next = completeWork(current, completedWork, subtreeRenderLanes);
1578        // Update render duration assuming we didn't error.
1579        stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);
1580      }
1581      resetCurrentDebugFiberInDEV();
1582
1583      if (next !== null) {
1584        // Completing this fiber spawned new work. Work on that next.
1585        workInProgress = next;
1586        return;
1587      }
1588    } else {
1589      // This fiber did not complete because something threw. Pop values off
1590      // the stack without entering the complete phase. If this is a boundary,
1591      // capture values if possible.
1592      const next = unwindWork(completedWork, subtreeRenderLanes);
1593
1594      // Because this fiber did not complete, don't reset its lanes.
1595
1596      if (next !== null) {
1597        // If completing this work spawned new work, do that next. We'll come
1598        // back here again.
1599        // Since we're restarting, remove anything that is not a host effect
1600        // from the effect tag.
1601        next.flags &= HostEffectMask;
1602        workInProgress = next;
1603        return;
1604      }
1605
1606      if (
1607        enableProfilerTimer &&
1608        (completedWork.mode & ProfileMode) !== NoMode
1609      ) {
1610        // Record the render duration for the fiber that errored.
1611        stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);
1612
1613        // Include the time spent working on failed children before continuing.
1614        let actualDuration = completedWork.actualDuration;
1615        let child = completedWork.child;
1616        while (child !== null) {
1617          actualDuration += child.actualDuration;
1618          child = child.sibling;
1619        }
1620        completedWork.actualDuration = actualDuration;
1621      }
1622
1623      if (returnFiber !== null) {
1624        // Mark the parent fiber as incomplete and clear its subtree flags.
1625        returnFiber.flags |= Incomplete;
1626        returnFiber.subtreeFlags = NoFlags;
1627        returnFiber.deletions = null;
1628      }
1629    }
1630
1631    const siblingFiber = completedWork.sibling;
1632    if (siblingFiber !== null) {
1633      // If there is more work to do in this returnFiber, do that next.
1634      workInProgress = siblingFiber;
1635      return;
1636    }
1637    // Otherwise, return to the parent
1638    completedWork = returnFiber;
1639    // Update the next thing we're working on in case something throws.
1640    workInProgress = completedWork;
1641  } while (completedWork !== null);
1642
1643  // We've reached the root.
1644  if (workInProgressRootExitStatus === RootIncomplete) {
1645    workInProgressRootExitStatus = RootCompleted;
1646  }
1647}
1648
1649function commitRoot(root) {
1650  // TODO: This no longer makes any sense. We already wrap the mutation and
1651  // layout phases. Should be able to remove.
1652  const previousUpdateLanePriority = getCurrentUpdatePriority();
1653  const prevTransition = ReactCurrentBatchConfig.transition;
1654  try {
1655    ReactCurrentBatchConfig.transition = 0;
1656    setCurrentUpdatePriority(DiscreteEventPriority);
1657    commitRootImpl(root, previousUpdateLanePriority);
1658  } finally {
1659    ReactCurrentBatchConfig.transition = prevTransition;
1660    setCurrentUpdatePriority(previousUpdateLanePriority);
1661  }
1662
1663  return null;
1664}
1665
1666function commitRootImpl(root, renderPriorityLevel) {
1667  do {
1668    // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which
1669    // means `flushPassiveEffects` will sometimes result in additional
1670    // passive effects. So we need to keep flushing in a loop until there are
1671    // no more pending effects.
1672    // TODO: Might be better if `flushPassiveEffects` did not automatically
1673    // flush synchronous work at the end, to avoid factoring hazards like this.
1674    flushPassiveEffects();
1675  } while (rootWithPendingPassiveEffects !== null);
1676  flushRenderPhaseStrictModeWarningsInDEV();
1677
1678  invariant(
1679    (executionContext & (RenderContext | CommitContext)) === NoContext,
1680    'Should not already be working.',
1681  );
1682
1683  const finishedWork = root.finishedWork;
1684  const lanes = root.finishedLanes;
1685
1686  if (__DEV__) {
1687    if (enableDebugTracing) {
1688      logCommitStarted(lanes);
1689    }
1690  }
1691
1692  if (enableSchedulingProfiler) {
1693    markCommitStarted(lanes);
1694  }
1695
1696  if (finishedWork === null) {
1697    if (__DEV__) {
1698      if (enableDebugTracing) {
1699        logCommitStopped();
1700      }
1701    }
1702
1703    if (enableSchedulingProfiler) {
1704      markCommitStopped();
1705    }
1706
1707    return null;
1708  } else {
1709    if (__DEV__) {
1710      if (lanes === NoLanes) {
1711        console.error(
1712          'root.finishedLanes should not be empty during a commit. This is a ' +
1713            'bug in React.',
1714        );
1715      }
1716    }
1717  }
1718  root.finishedWork = null;
1719  root.finishedLanes = NoLanes;
1720
1721  invariant(
1722    finishedWork !== root.current,
1723    'Cannot commit the same tree as before. This error is likely caused by ' +
1724      'a bug in React. Please file an issue.',
1725  );
1726
1727  // commitRoot never returns a continuation; it always finishes synchronously.
1728  // So we can clear these now to allow a new callback to be scheduled.
1729  root.callbackNode = null;
1730  root.callbackPriority = NoLane;
1731
1732  // Update the first and last pending times on this root. The new first
1733  // pending time is whatever is left on the root fiber.
1734  let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
1735  markRootFinished(root, remainingLanes);
1736
1737  if (root === workInProgressRoot) {
1738    // We can reset these now that they are finished.
1739    workInProgressRoot = null;
1740    workInProgress = null;
1741    workInProgressRootRenderLanes = NoLanes;
1742  } else {
1743    // This indicates that the last root we worked on is not the same one that
1744    // we're committing now. This most commonly happens when a suspended root
1745    // times out.
1746  }
1747
1748  // If there are pending passive effects, schedule a callback to process them.
1749  // Do this as early as possible, so it is queued before anything else that
1750  // might get scheduled in the commit phase. (See #16714.)
1751  // TODO: Delete all other places that schedule the passive effect callback
1752  // They're redundant.
1753  if (
1754    (finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||
1755    (finishedWork.flags & PassiveMask) !== NoFlags
1756  ) {
1757    if (!rootDoesHavePassiveEffects) {
1758      rootDoesHavePassiveEffects = true;
1759      scheduleCallback(NormalSchedulerPriority, () => {
1760        flushPassiveEffects();
1761        return null;
1762      });
1763    }
1764  }
1765
1766  // Check if there are any effects in the whole tree.
1767  // TODO: This is left over from the effect list implementation, where we had
1768  // to check for the existence of `firstEffect` to satisfy Flow. I think the
1769  // only other reason this optimization exists is because it affects profiling.
1770  // Reconsider whether this is necessary.
1771  const subtreeHasEffects =
1772    (finishedWork.subtreeFlags &
1773      (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
1774    NoFlags;
1775  const rootHasEffect =
1776    (finishedWork.flags &
1777      (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
1778    NoFlags;
1779
1780  if (subtreeHasEffects || rootHasEffect) {
1781    const prevTransition = ReactCurrentBatchConfig.transition;
1782    ReactCurrentBatchConfig.transition = 0;
1783    const previousPriority = getCurrentUpdatePriority();
1784    setCurrentUpdatePriority(DiscreteEventPriority);
1785
1786    const prevExecutionContext = executionContext;
1787    executionContext |= CommitContext;
1788
1789    // Reset this to null before calling lifecycles
1790    ReactCurrentOwner.current = null;
1791
1792    // The commit phase is broken into several sub-phases. We do a separate pass
1793    // of the effect list for each phase: all mutation effects come before all
1794    // layout effects, and so on.
1795
1796    // The first phase a "before mutation" phase. We use this phase to read the
1797    // state of the host tree right before we mutate it. This is where
1798    // getSnapshotBeforeUpdate is called.
1799    const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(
1800      root,
1801      finishedWork,
1802    );
1803
1804    if (enableProfilerTimer) {
1805      // Mark the current commit time to be shared by all Profilers in this
1806      // batch. This enables them to be grouped later.
1807      recordCommitTime();
1808    }
1809
1810    if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
1811      // Track the root here, rather than in commitLayoutEffects(), because of ref setters.
1812      // Updates scheduled during ref detachment should also be flagged.
1813      rootCommittingMutationOrLayoutEffects = root;
1814    }
1815
1816    // The next phase is the mutation phase, where we mutate the host tree.
1817    commitMutationEffects(root, finishedWork, lanes);
1818
1819    if (enableCreateEventHandleAPI) {
1820      if (shouldFireAfterActiveInstanceBlur) {
1821        afterActiveInstanceBlur();
1822      }
1823    }
1824    resetAfterCommit(root.containerInfo);
1825
1826    // The work-in-progress tree is now the current tree. This must come after
1827    // the mutation phase, so that the previous tree is still current during
1828    // componentWillUnmount, but before the layout phase, so that the finished
1829    // work is current during componentDidMount/Update.
1830    root.current = finishedWork;
1831
1832    // The next phase is the layout phase, where we call effects that read
1833    // the host tree after it's been mutated. The idiomatic use case for this is
1834    // layout, but class component lifecycles also fire here for legacy reasons.
1835    if (__DEV__) {
1836      if (enableDebugTracing) {
1837        logLayoutEffectsStarted(lanes);
1838      }
1839    }
1840    if (enableSchedulingProfiler) {
1841      markLayoutEffectsStarted(lanes);
1842    }
1843    commitLayoutEffects(finishedWork, root, lanes);
1844    if (__DEV__) {
1845      if (enableDebugTracing) {
1846        logLayoutEffectsStopped();
1847      }
1848    }
1849
1850    if (enableSchedulingProfiler) {
1851      markLayoutEffectsStopped();
1852    }
1853
1854    if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
1855      rootCommittingMutationOrLayoutEffects = null;
1856    }
1857
1858    // Tell Scheduler to yield at the end of the frame, so the browser has an
1859    // opportunity to paint.
1860    requestPaint();
1861
1862    executionContext = prevExecutionContext;
1863
1864    // Reset the priority to the previous non-sync value.
1865    setCurrentUpdatePriority(previousPriority);
1866    ReactCurrentBatchConfig.transition = prevTransition;
1867  } else {
1868    // No effects.
1869    root.current = finishedWork;
1870    // Measure these anyway so the flamegraph explicitly shows that there were
1871    // no effects.
1872    // TODO: Maybe there's a better way to report this.
1873    if (enableProfilerTimer) {
1874      recordCommitTime();
1875    }
1876  }
1877
1878  const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;
1879
1880  if (rootDoesHavePassiveEffects) {
1881    // This commit has passive effects. Stash a reference to them. But don't
1882    // schedule a callback until after flushing layout work.
1883    rootDoesHavePassiveEffects = false;
1884    rootWithPendingPassiveEffects = root;
1885    pendingPassiveEffectsLanes = lanes;
1886  }
1887
1888  // Read this again, since an effect might have updated it
1889  remainingLanes = root.pendingLanes;
1890
1891  // Check if there's remaining work on this root
1892  if (remainingLanes === NoLanes) {
1893    // If there's no remaining work, we can clear the set of already failed
1894    // error boundaries.
1895    legacyErrorBoundariesThatAlreadyFailed = null;
1896  }
1897
1898  if (__DEV__ && enableStrictEffects) {
1899    if (!rootDidHavePassiveEffects) {
1900      commitDoubleInvokeEffectsInDEV(root.current, false);
1901    }
1902  }
1903
1904  if (includesSomeLane(remainingLanes, (SyncLane: Lane))) {
1905    if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {
1906      markNestedUpdateScheduled();
1907    }
1908
1909    // Count the number of times the root synchronously re-renders without
1910    // finishing. If there are too many, it indicates an infinite update loop.
1911    if (root === rootWithNestedUpdates) {
1912      nestedUpdateCount++;
1913    } else {
1914      nestedUpdateCount = 0;
1915      rootWithNestedUpdates = root;
1916    }
1917  } else {
1918    nestedUpdateCount = 0;
1919  }
1920
1921  onCommitRootDevTools(finishedWork.stateNode, renderPriorityLevel);
1922
1923  if (enableUpdaterTracking) {
1924    if (isDevToolsPresent) {
1925      root.memoizedUpdaters.clear();
1926    }
1927  }
1928
1929  if (__DEV__) {
1930    onCommitRootTestSelector();
1931  }
1932
1933  // Always call this before exiting `commitRoot`, to ensure that any
1934  // additional work on this root is scheduled.
1935  ensureRootIsScheduled(root, now());
1936
1937  if (hasUncaughtError) {
1938    hasUncaughtError = false;
1939    const error = firstUncaughtError;
1940    firstUncaughtError = null;
1941    throw error;
1942  }
1943
1944  // If the passive effects are the result of a discrete render, flush them
1945  // synchronously at the end of the current task so that the result is
1946  // immediately observable. Otherwise, we assume that they are not
1947  // order-dependent and do not need to be observed by external systems, so we
1948  // can wait until after paint.
1949  // TODO: We can optimize this by not scheduling the callback earlier. Since we
1950  // currently schedule the callback in multiple places, will wait until those
1951  // are consolidated.
1952  if (
1953    includesSomeLane(pendingPassiveEffectsLanes, SyncLane) &&
1954    root.tag !== LegacyRoot
1955  ) {
1956    flushPassiveEffects();
1957  }
1958
1959  // If layout work was scheduled, flush it now.
1960  flushSyncCallbacks();
1961
1962  if (__DEV__) {
1963    if (enableDebugTracing) {
1964      logCommitStopped();
1965    }
1966  }
1967
1968  if (enableSchedulingProfiler) {
1969    markCommitStopped();
1970  }
1971
1972  return null;
1973}
1974
1975export function flushPassiveEffects(): boolean {
1976  // Returns whether passive effects were flushed.
1977  // TODO: Combine this check with the one in flushPassiveEFfectsImpl. We should
1978  // probably just combine the two functions. I believe they were only separate
1979  // in the first place because we used to wrap it with
1980  // `Scheduler.runWithPriority`, which accepts a function. But now we track the
1981  // priority within React itself, so we can mutate the variable directly.
1982  if (rootWithPendingPassiveEffects !== null) {
1983    const renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes);
1984    const priority = lowerEventPriority(DefaultEventPriority, renderPriority);
1985    const prevTransition = ReactCurrentBatchConfig.transition;
1986    const previousPriority = getCurrentUpdatePriority();
1987    try {
1988      ReactCurrentBatchConfig.transition = 0;
1989      setCurrentUpdatePriority(priority);
1990      return flushPassiveEffectsImpl();
1991    } finally {
1992      setCurrentUpdatePriority(previousPriority);
1993      ReactCurrentBatchConfig.transition = prevTransition;
1994    }
1995  }
1996  return false;
1997}
1998
1999export function enqueuePendingPassiveProfilerEffect(fiber: Fiber): void {
2000  if (enableProfilerTimer && enableProfilerCommitHooks) {
2001    pendingPassiveProfilerEffects.push(fiber);
2002    if (!rootDoesHavePassiveEffects) {
2003      rootDoesHavePassiveEffects = true;
2004      scheduleCallback(NormalSchedulerPriority, () => {
2005        flushPassiveEffects();
2006        return null;
2007      });
2008    }
2009  }
2010}
2011
2012function flushPassiveEffectsImpl() {
2013  if (rootWithPendingPassiveEffects === null) {
2014    return false;
2015  }
2016
2017  const root = rootWithPendingPassiveEffects;
2018  const lanes = pendingPassiveEffectsLanes;
2019  rootWithPendingPassiveEffects = null;
2020  // TODO: This is sometimes out of sync with rootWithPendingPassiveEffects.
2021  // Figure out why and fix it. It's not causing any known issues (probably
2022  // because it's only used for profiling), but it's a refactor hazard.
2023  pendingPassiveEffectsLanes = NoLanes;
2024
2025  invariant(
2026    (executionContext & (RenderContext | CommitContext)) === NoContext,
2027    'Cannot flush passive effects while already rendering.',
2028  );
2029
2030  if (__DEV__) {
2031    if (enableDebugTracing) {
2032      logPassiveEffectsStarted(lanes);
2033    }
2034  }
2035
2036  if (enableSchedulingProfiler) {
2037    markPassiveEffectsStarted(lanes);
2038  }
2039
2040  const prevExecutionContext = executionContext;
2041  executionContext |= CommitContext;
2042
2043  commitPassiveUnmountEffects(root.current);
2044  commitPassiveMountEffects(root, root.current);
2045
2046  // TODO: Move to commitPassiveMountEffects
2047  if (enableProfilerTimer && enableProfilerCommitHooks) {
2048    const profilerEffects = pendingPassiveProfilerEffects;
2049    pendingPassiveProfilerEffects = [];
2050    for (let i = 0; i < profilerEffects.length; i++) {
2051      const fiber = ((profilerEffects[i]: any): Fiber);
2052      commitPassiveEffectDurations(root, fiber);
2053    }
2054  }
2055
2056  if (__DEV__) {
2057    if (enableDebugTracing) {
2058      logPassiveEffectsStopped();
2059    }
2060  }
2061
2062  if (enableSchedulingProfiler) {
2063    markPassiveEffectsStopped();
2064  }
2065
2066  if (__DEV__ && enableStrictEffects) {
2067    commitDoubleInvokeEffectsInDEV(root.current, true);
2068  }
2069
2070  executionContext = prevExecutionContext;
2071
2072  flushSyncCallbacks();
2073
2074  // If additional passive effects were scheduled, increment a counter. If this
2075  // exceeds the limit, we'll fire a warning.
2076  nestedPassiveUpdateCount =
2077    rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;
2078
2079  // TODO: Move to commitPassiveMountEffects
2080  onPostCommitRootDevTools(root);
2081  if (enableProfilerTimer && enableProfilerCommitHooks) {
2082    const stateNode = root.current.stateNode;
2083    stateNode.effectDuration = 0;
2084    stateNode.passiveEffectDuration = 0;
2085  }
2086
2087  return true;
2088}
2089
2090export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {
2091  return (
2092    legacyErrorBoundariesThatAlreadyFailed !== null &&
2093    legacyErrorBoundariesThatAlreadyFailed.has(instance)
2094  );
2095}
2096
2097export function markLegacyErrorBoundaryAsFailed(instance: mixed) {
2098  if (legacyErrorBoundariesThatAlreadyFailed === null) {
2099    legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);
2100  } else {
2101    legacyErrorBoundariesThatAlreadyFailed.add(instance);
2102  }
2103}
2104
2105function prepareToThrowUncaughtError(error: mixed) {
2106  if (!hasUncaughtError) {
2107    hasUncaughtError = true;
2108    firstUncaughtError = error;
2109  }
2110}
2111export const onUncaughtError = prepareToThrowUncaughtError;
2112
2113function captureCommitPhaseErrorOnRoot(
2114  rootFiber: Fiber,
2115  sourceFiber: Fiber,
2116  error: mixed,
2117) {
2118  const errorInfo = createCapturedValue(error, sourceFiber);
2119  const update = createRootErrorUpdate(rootFiber, errorInfo, (SyncLane: Lane));
2120  enqueueUpdate(rootFiber, update, (SyncLane: Lane));
2121  const eventTime = requestEventTime();
2122  const root = markUpdateLaneFromFiberToRoot(rootFiber, (SyncLane: Lane));
2123  if (root !== null) {
2124    markRootUpdated(root, SyncLane, eventTime);
2125    ensureRootIsScheduled(root, eventTime);
2126  }
2127}
2128
2129export function captureCommitPhaseError(
2130  sourceFiber: Fiber,
2131  nearestMountedAncestor: Fiber | null,
2132  error: mixed,
2133) {
2134  if (sourceFiber.tag === HostRoot) {
2135    // Error was thrown at the root. There is no parent, so the root
2136    // itself should capture it.
2137    captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);
2138    return;
2139  }
2140
2141  let fiber = null;
2142  if (skipUnmountedBoundaries) {
2143    fiber = nearestMountedAncestor;
2144  } else {
2145    fiber = sourceFiber.return;
2146  }
2147
2148  while (fiber !== null) {
2149    if (fiber.tag === HostRoot) {
2150      captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);
2151      return;
2152    } else if (fiber.tag === ClassComponent) {
2153      const ctor = fiber.type;
2154      const instance = fiber.stateNode;
2155      if (
2156        typeof ctor.getDerivedStateFromError === 'function' ||
2157        (typeof instance.componentDidCatch === 'function' &&
2158          !isAlreadyFailedLegacyErrorBoundary(instance))
2159      ) {
2160        const errorInfo = createCapturedValue(error, sourceFiber);
2161        const update = createClassErrorUpdate(
2162          fiber,
2163          errorInfo,
2164          (SyncLane: Lane),
2165        );
2166        enqueueUpdate(fiber, update, (SyncLane: Lane));
2167        const eventTime = requestEventTime();
2168        const root = markUpdateLaneFromFiberToRoot(fiber, (SyncLane: Lane));
2169        if (root !== null) {
2170          markRootUpdated(root, SyncLane, eventTime);
2171          ensureRootIsScheduled(root, eventTime);
2172        }
2173        return;
2174      }
2175    }
2176    fiber = fiber.return;
2177  }
2178
2179  if (__DEV__) {
2180    // TODO: Until we re-land skipUnmountedBoundaries (see #20147), this warning
2181    // will fire for errors that are thrown by destroy functions inside deleted
2182    // trees. What it should instead do is propagate the error to the parent of
2183    // the deleted tree. In the meantime, do not add this warning to the
2184    // allowlist; this is only for our internal use.
2185    console.error(
2186      'Internal React error: Attempted to capture a commit phase error ' +
2187        'inside a detached tree. This indicates a bug in React. Likely ' +
2188        'causes include deleting the same fiber more than once, committing an ' +
2189        'already-finished tree, or an inconsistent return pointer.\n\n' +
2190        'Error message:\n\n%s',
2191      error,
2192    );
2193  }
2194}
2195
2196export function pingSuspendedRoot(
2197  root: FiberRoot,
2198  wakeable: Wakeable,
2199  pingedLanes: Lanes,
2200) {
2201  const pingCache = root.pingCache;
2202  if (pingCache !== null) {
2203    // The wakeable resolved, so we no longer need to memoize, because it will
2204    // never be thrown again.
2205    pingCache.delete(wakeable);
2206  }
2207
2208  const eventTime = requestEventTime();
2209  markRootPinged(root, pingedLanes, eventTime);
2210
2211  if (
2212    workInProgressRoot === root &&
2213    isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)
2214  ) {
2215    // Received a ping at the same priority level at which we're currently
2216    // rendering. We might want to restart this render. This should mirror
2217    // the logic of whether or not a root suspends once it completes.
2218
2219    // TODO: If we're rendering sync either due to Sync, Batched or expired,
2220    // we should probably never restart.
2221
2222    // If we're suspended with delay, or if it's a retry, we'll always suspend
2223    // so we can always restart.
2224    if (
2225      workInProgressRootExitStatus === RootSuspendedWithDelay ||
2226      (workInProgressRootExitStatus === RootSuspended &&
2227        includesOnlyRetries(workInProgressRootRenderLanes) &&
2228        now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)
2229    ) {
2230      // Restart from the root.
2231      prepareFreshStack(root, NoLanes);
2232    } else {
2233      // Even though we can't restart right now, we might get an
2234      // opportunity later. So we mark this render as having a ping.
2235      workInProgressRootPingedLanes = mergeLanes(
2236        workInProgressRootPingedLanes,
2237        pingedLanes,
2238      );
2239    }
2240  }
2241
2242  ensureRootIsScheduled(root, eventTime);
2243}
2244
2245function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {
2246  // The boundary fiber (a Suspense component or SuspenseList component)
2247  // previously was rendered in its fallback state. One of the promises that
2248  // suspended it has resolved, which means at least part of the tree was
2249  // likely unblocked. Try rendering again, at a new lanes.
2250  if (retryLane === NoLane) {
2251    // TODO: Assign this to `suspenseState.retryLane`? to avoid
2252    // unnecessary entanglement?
2253    retryLane = requestRetryLane(boundaryFiber);
2254  }
2255  // TODO: Special case idle priority?
2256  const eventTime = requestEventTime();
2257  const root = markUpdateLaneFromFiberToRoot(boundaryFiber, retryLane);
2258  if (root !== null) {
2259    markRootUpdated(root, retryLane, eventTime);
2260    ensureRootIsScheduled(root, eventTime);
2261  }
2262}
2263
2264export function retryDehydratedSuspenseBoundary(boundaryFiber: Fiber) {
2265  const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;
2266  let retryLane = NoLane;
2267  if (suspenseState !== null) {
2268    retryLane = suspenseState.retryLane;
2269  }
2270  retryTimedOutBoundary(boundaryFiber, retryLane);
2271}
2272
2273export function resolveRetryWakeable(boundaryFiber: Fiber, wakeable: Wakeable) {
2274  let retryLane = NoLane; // Default
2275  let retryCache: WeakSet<Wakeable> | Set<Wakeable> | null;
2276  if (enableSuspenseServerRenderer) {
2277    switch (boundaryFiber.tag) {
2278      case SuspenseComponent:
2279        retryCache = boundaryFiber.stateNode;
2280        const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;
2281        if (suspenseState !== null) {
2282          retryLane = suspenseState.retryLane;
2283        }
2284        break;
2285      case SuspenseListComponent:
2286        retryCache = boundaryFiber.stateNode;
2287        break;
2288      default:
2289        invariant(
2290          false,
2291          'Pinged unknown suspense boundary type. ' +
2292            'This is probably a bug in React.',
2293        );
2294    }
2295  } else {
2296    retryCache = boundaryFiber.stateNode;
2297  }
2298
2299  if (retryCache !== null) {
2300    // The wakeable resolved, so we no longer need to memoize, because it will
2301    // never be thrown again.
2302    retryCache.delete(wakeable);
2303  }
2304
2305  retryTimedOutBoundary(boundaryFiber, retryLane);
2306}
2307
2308// Computes the next Just Noticeable Difference (JND) boundary.
2309// The theory is that a person can't tell the difference between small differences in time.
2310// Therefore, if we wait a bit longer than necessary that won't translate to a noticeable
2311// difference in the experience. However, waiting for longer might mean that we can avoid
2312// showing an intermediate loading state. The longer we have already waited, the harder it
2313// is to tell small differences in time. Therefore, the longer we've already waited,
2314// the longer we can wait additionally. At some point we have to give up though.
2315// We pick a train model where the next boundary commits at a consistent schedule.
2316// These particular numbers are vague estimates. We expect to adjust them based on research.
2317function jnd(timeElapsed: number) {
2318  return timeElapsed < 120
2319    ? 120
2320    : timeElapsed < 480
2321    ? 480
2322    : timeElapsed < 1080
2323    ? 1080
2324    : timeElapsed < 1920
2325    ? 1920
2326    : timeElapsed < 3000
2327    ? 3000
2328    : timeElapsed < 4320
2329    ? 4320
2330    : ceil(timeElapsed / 1960) * 1960;
2331}
2332
2333function checkForNestedUpdates() {
2334  if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
2335    nestedUpdateCount = 0;
2336    rootWithNestedUpdates = null;
2337    invariant(
2338      false,
2339      'Maximum update depth exceeded. This can happen when a component ' +
2340        'repeatedly calls setState inside componentWillUpdate or ' +
2341        'componentDidUpdate. React limits the number of nested updates to ' +
2342        'prevent infinite loops.',
2343    );
2344  }
2345
2346  if (__DEV__) {
2347    if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {
2348      nestedPassiveUpdateCount = 0;
2349      console.error(
2350        'Maximum update depth exceeded. This can happen when a component ' +
2351          "calls setState inside useEffect, but useEffect either doesn't " +
2352          'have a dependency array, or one of the dependencies changes on ' +
2353          'every render.',
2354      );
2355    }
2356  }
2357}
2358
2359function flushRenderPhaseStrictModeWarningsInDEV() {
2360  if (__DEV__) {
2361    ReactStrictModeWarnings.flushLegacyContextWarning();
2362
2363    if (warnAboutDeprecatedLifecycles) {
2364      ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();
2365    }
2366  }
2367}
2368
2369function commitDoubleInvokeEffectsInDEV(
2370  fiber: Fiber,
2371  hasPassiveEffects: boolean,
2372) {
2373  if (__DEV__ && enableStrictEffects) {
2374    // TODO (StrictEffects) Should we set a marker on the root if it contains strict effects
2375    // so we don't traverse unnecessarily? similar to subtreeFlags but just at the root level.
2376    // Maybe not a big deal since this is DEV only behavior.
2377
2378    setCurrentDebugFiberInDEV(fiber);
2379    invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectUnmountInDEV);
2380    if (hasPassiveEffects) {
2381      invokeEffectsInDev(
2382        fiber,
2383        MountPassiveDev,
2384        invokePassiveEffectUnmountInDEV,
2385      );
2386    }
2387
2388    invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectMountInDEV);
2389    if (hasPassiveEffects) {
2390      invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectMountInDEV);
2391    }
2392    resetCurrentDebugFiberInDEV();
2393  }
2394}
2395
2396function invokeEffectsInDev(
2397  firstChild: Fiber,
2398  fiberFlags: Flags,
2399  invokeEffectFn: (fiber: Fiber) => void,
2400): void {
2401  if (__DEV__ && enableStrictEffects) {
2402    // We don't need to re-check StrictEffectsMode here.
2403    // This function is only called if that check has already passed.
2404
2405    let current = firstChild;
2406    let subtreeRoot = null;
2407    while (current !== null) {
2408      const primarySubtreeFlag = current.subtreeFlags & fiberFlags;
2409      if (
2410        current !== subtreeRoot &&
2411        current.child !== null &&
2412        primarySubtreeFlag !== NoFlags
2413      ) {
2414        current = current.child;
2415      } else {
2416        if ((current.flags & fiberFlags) !== NoFlags) {
2417          invokeEffectFn(current);
2418        }
2419
2420        if (current.sibling !== null) {
2421          current = current.sibling;
2422        } else {
2423          current = subtreeRoot = current.return;
2424        }
2425      }
2426    }
2427  }
2428}
2429
2430let didWarnStateUpdateForNotYetMountedComponent: Set<string> | null = null;
2431function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber) {
2432  if (__DEV__) {
2433    if ((executionContext & RenderContext) !== NoContext) {
2434      // We let the other warning about render phase updates deal with this one.
2435      return;
2436    }
2437
2438    if (!(fiber.mode & ConcurrentMode)) {
2439      return;
2440    }
2441
2442    const tag = fiber.tag;
2443    if (
2444      tag !== IndeterminateComponent &&
2445      tag !== HostRoot &&
2446      tag !== ClassComponent &&
2447      tag !== FunctionComponent &&
2448      tag !== ForwardRef &&
2449      tag !== MemoComponent &&
2450      tag !== SimpleMemoComponent
2451    ) {
2452      // Only warn for user-defined components, not internal ones like Suspense.
2453      return;
2454    }
2455
2456    // We show the whole stack but dedupe on the top component's name because
2457    // the problematic code almost always lies inside that component.
2458    const componentName = getComponentNameFromFiber(fiber) || 'ReactComponent';
2459    if (didWarnStateUpdateForNotYetMountedComponent !== null) {
2460      if (didWarnStateUpdateForNotYetMountedComponent.has(componentName)) {
2461        return;
2462      }
2463      didWarnStateUpdateForNotYetMountedComponent.add(componentName);
2464    } else {
2465      didWarnStateUpdateForNotYetMountedComponent = new Set([componentName]);
2466    }
2467
2468    const previousFiber = ReactCurrentFiberCurrent;
2469    try {
2470      setCurrentDebugFiberInDEV(fiber);
2471      console.error(
2472        "Can't perform a React state update on a component that hasn't mounted yet. " +
2473          'This indicates that you have a side-effect in your render function that ' +
2474          'asynchronously later calls tries to update the component. Move this work to ' +
2475          'useEffect instead.',
2476      );
2477    } finally {
2478      if (previousFiber) {
2479        setCurrentDebugFiberInDEV(fiber);
2480      } else {
2481        resetCurrentDebugFiberInDEV();
2482      }
2483    }
2484  }
2485}
2486
2487let beginWork;
2488if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
2489  const dummyFiber = null;
2490  beginWork = (current, unitOfWork, lanes) => {
2491    // If a component throws an error, we replay it again in a synchronously
2492    // dispatched event, so that the debugger will treat it as an uncaught
2493    // error See ReactErrorUtils for more information.
2494
2495    // Before entering the begin phase, copy the work-in-progress onto a dummy
2496    // fiber. If beginWork throws, we'll use this to reset the state.
2497    const originalWorkInProgressCopy = assignFiberPropertiesInDEV(
2498      dummyFiber,
2499      unitOfWork,
2500    );
2501    try {
2502      return originalBeginWork(current, unitOfWork, lanes);
2503    } catch (originalError) {
2504      if (
2505        originalError !== null &&
2506        typeof originalError === 'object' &&
2507        typeof originalError.then === 'function'
2508      ) {
2509        // Don't replay promises. Treat everything else like an error.
2510        throw originalError;
2511      }
2512
2513      // Keep this code in sync with handleError; any changes here must have
2514      // corresponding changes there.
2515      resetContextDependencies();
2516      resetHooksAfterThrow();
2517      // Don't reset current debug fiber, since we're about to work on the
2518      // same fiber again.
2519
2520      // Unwind the failed stack frame
2521      unwindInterruptedWork(unitOfWork, workInProgressRootRenderLanes);
2522
2523      // Restore the original properties of the fiber.
2524      assignFiberPropertiesInDEV(unitOfWork, originalWorkInProgressCopy);
2525
2526      if (enableProfilerTimer && unitOfWork.mode & ProfileMode) {
2527        // Reset the profiler timer.
2528        startProfilerTimer(unitOfWork);
2529      }
2530
2531      // Run beginWork again.
2532      invokeGuardedCallback(
2533        null,
2534        originalBeginWork,
2535        null,
2536        current,
2537        unitOfWork,
2538        lanes,
2539      );
2540
2541      if (hasCaughtError()) {
2542        const replayError = clearCaughtError();
2543        if (
2544          typeof replayError === 'object' &&
2545          replayError !== null &&
2546          replayError._suppressLogging &&
2547          typeof originalError === 'object' &&
2548          originalError !== null &&
2549          !originalError._suppressLogging
2550        ) {
2551          // If suppressed, let the flag carry over to the original error which is the one we'll rethrow.
2552          originalError._suppressLogging = true;
2553        }
2554      }
2555      // We always throw the original error in case the second render pass is not idempotent.
2556      // This can happen if a memoized function or CommonJS module doesn't throw after first invokation.
2557      throw originalError;
2558    }
2559  };
2560} else {
2561  beginWork = originalBeginWork;
2562}
2563
2564let didWarnAboutUpdateInRender = false;
2565let didWarnAboutUpdateInRenderForAnotherComponent;
2566if (__DEV__) {
2567  didWarnAboutUpdateInRenderForAnotherComponent = new Set();
2568}
2569
2570function warnAboutRenderPhaseUpdatesInDEV(fiber) {
2571  if (__DEV__) {
2572    if (
2573      ReactCurrentDebugFiberIsRenderingInDEV &&
2574      (executionContext & RenderContext) !== NoContext &&
2575      !getIsUpdatingOpaqueValueInRenderPhaseInDEV()
2576    ) {
2577      switch (fiber.tag) {
2578        case FunctionComponent:
2579        case ForwardRef:
2580        case SimpleMemoComponent: {
2581          const renderingComponentName =
2582            (workInProgress && getComponentNameFromFiber(workInProgress)) ||
2583            'Unknown';
2584          // Dedupe by the rendering component because it's the one that needs to be fixed.
2585          const dedupeKey = renderingComponentName;
2586          if (!didWarnAboutUpdateInRenderForAnotherComponent.has(dedupeKey)) {
2587            didWarnAboutUpdateInRenderForAnotherComponent.add(dedupeKey);
2588            const setStateComponentName =
2589              getComponentNameFromFiber(fiber) || 'Unknown';
2590            console.error(
2591              'Cannot update a component (`%s`) while rendering a ' +
2592                'different component (`%s`). To locate the bad setState() call inside `%s`, ' +
2593                'follow the stack trace as described in https://reactjs.org/link/setstate-in-render',
2594              setStateComponentName,
2595              renderingComponentName,
2596              renderingComponentName,
2597            );
2598          }
2599          break;
2600        }
2601        case ClassComponent: {
2602          if (!didWarnAboutUpdateInRender) {
2603            console.error(
2604              'Cannot update during an existing state transition (such as ' +
2605                'within `render`). Render methods should be a pure ' +
2606                'function of props and state.',
2607            );
2608            didWarnAboutUpdateInRender = true;
2609          }
2610          break;
2611        }
2612      }
2613    }
2614  }
2615}
2616
2617export function restorePendingUpdaters(root: FiberRoot, lanes: Lanes): void {
2618  if (enableUpdaterTracking) {
2619    if (isDevToolsPresent) {
2620      const memoizedUpdaters = root.memoizedUpdaters;
2621      memoizedUpdaters.forEach(schedulingFiber => {
2622        addFiberToLanesMap(root, schedulingFiber, lanes);
2623      });
2624
2625      // This function intentionally does not clear memoized updaters.
2626      // Those may still be relevant to the current commit
2627      // and a future one (e.g. Suspense).
2628    }
2629  }
2630}
2631
2632const fakeActCallbackNode = {};
2633function scheduleCallback(priorityLevel, callback) {
2634  if (__DEV__) {
2635    // If we're currently inside an `act` scope, bypass Scheduler and push to
2636    // the `act` queue instead.
2637    const actQueue = ReactCurrentActQueue.current;
2638    if (actQueue !== null) {
2639      actQueue.push(callback);
2640      return fakeActCallbackNode;
2641    } else {
2642      return Scheduler_scheduleCallback(priorityLevel, callback);
2643    }
2644  } else {
2645    // In production, always call Scheduler. This function will be stripped out.
2646    return Scheduler_scheduleCallback(priorityLevel, callback);
2647  }
2648}
2649
2650function cancelCallback(callbackNode) {
2651  if (__DEV__ && callbackNode === fakeActCallbackNode) {
2652    return;
2653  }
2654  // In production, always call Scheduler. This function will be stripped out.
2655  return Scheduler_cancelCallback(callbackNode);
2656}
2657
2658function shouldForceFlushFallbacksInDEV() {
2659  // Never force flush in production. This function should get stripped out.
2660  return __DEV__ && ReactCurrentActQueue.current !== null;
2661}
2662
2663export function warnIfNotCurrentlyActingEffectsInDEV(fiber: Fiber): void {
2664  if (__DEV__) {
2665    if (
2666      warnsIfNotActing === true &&
2667      (fiber.mode & StrictLegacyMode) !== NoMode &&
2668      ReactCurrentActQueue.current === null &&
2669      // Our internal tests use a custom implementation of `act` that works by
2670      // mocking the Scheduler package. Disable the `act` warning.
2671      // TODO: Maybe the warning should be disabled by default, and then turned
2672      // on at the testing frameworks layer? Instead of what we do now, which
2673      // is check if a `jest` global is defined.
2674      ReactCurrentActQueue.disableActWarning === false
2675    ) {
2676      console.error(
2677        'An update to %s ran an effect, but was not wrapped in act(...).\n\n' +
2678          'When testing, code that causes React state updates should be ' +
2679          'wrapped into act(...):\n\n' +
2680          'act(() => {\n' +
2681          '  /* fire events that update state */\n' +
2682          '});\n' +
2683          '/* assert on the output */\n\n' +
2684          "This ensures that you're testing the behavior the user would see " +
2685          'in the browser.' +
2686          ' Learn more at https://reactjs.org/link/wrap-tests-with-act',
2687        getComponentNameFromFiber(fiber),
2688      );
2689    }
2690  }
2691}
2692
2693function warnIfNotCurrentlyActingUpdatesInDEV(fiber: Fiber): void {
2694  if (__DEV__) {
2695    if (
2696      warnsIfNotActing === true &&
2697      executionContext === NoContext &&
2698      ReactCurrentActQueue.current === null &&
2699      // Our internal tests use a custom implementation of `act` that works by
2700      // mocking the Scheduler package. Disable the `act` warning.
2701      // TODO: Maybe the warning should be disabled by default, and then turned
2702      // on at the testing frameworks layer? Instead of what we do now, which
2703      // is check if a `jest` global is defined.
2704      ReactCurrentActQueue.disableActWarning === false
2705    ) {
2706      const previousFiber = ReactCurrentFiberCurrent;
2707      try {
2708        setCurrentDebugFiberInDEV(fiber);
2709        console.error(
2710          'An update to %s inside a test was not wrapped in act(...).\n\n' +
2711            'When testing, code that causes React state updates should be ' +
2712            'wrapped into act(...):\n\n' +
2713            'act(() => {\n' +
2714            '  /* fire events that update state */\n' +
2715            '});\n' +
2716            '/* assert on the output */\n\n' +
2717            "This ensures that you're testing the behavior the user would see " +
2718            'in the browser.' +
2719            ' Learn more at https://reactjs.org/link/wrap-tests-with-act',
2720          getComponentNameFromFiber(fiber),
2721        );
2722      } finally {
2723        if (previousFiber) {
2724          setCurrentDebugFiberInDEV(fiber);
2725        } else {
2726          resetCurrentDebugFiberInDEV();
2727        }
2728      }
2729    }
2730  }
2731}
2732
2733export const warnIfNotCurrentlyActingUpdatesInDev = warnIfNotCurrentlyActingUpdatesInDEV;
2734
Full Screen