1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and 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. 
    
  10. import type {
    
  11.   Instance,
    
  12.   TextInstance,
    
  13.   SuspenseInstance,
    
  14.   Container,
    
  15.   ChildSet,
    
  16.   UpdatePayload,
    
  17.   HoistableRoot,
    
  18. } from './ReactFiberConfig';
    
  19. import type {Fiber, FiberRoot} from './ReactInternalTypes';
    
  20. import type {Lanes} from './ReactFiberLane';
    
  21. import {SyncLane} from './ReactFiberLane';
    
  22. import type {SuspenseState, RetryQueue} from './ReactFiberSuspenseComponent';
    
  23. import type {UpdateQueue} from './ReactFiberClassUpdateQueue';
    
  24. import type {FunctionComponentUpdateQueue} from './ReactFiberHooks';
    
  25. import type {Wakeable} from 'shared/ReactTypes';
    
  26. import {isOffscreenManual} from './ReactFiberActivityComponent';
    
  27. import type {
    
  28.   OffscreenState,
    
  29.   OffscreenInstance,
    
  30.   OffscreenQueue,
    
  31.   OffscreenProps,
    
  32. } from './ReactFiberActivityComponent';
    
  33. import type {HookFlags} from './ReactHookEffectTags';
    
  34. import type {Cache} from './ReactFiberCacheComponent';
    
  35. import type {RootState} from './ReactFiberRoot';
    
  36. import type {
    
  37.   Transition,
    
  38.   TracingMarkerInstance,
    
  39.   TransitionAbort,
    
  40. } from './ReactFiberTracingMarkerComponent';
    
  41. 
    
  42. import {
    
  43.   enableCreateEventHandleAPI,
    
  44.   enableProfilerTimer,
    
  45.   enableProfilerCommitHooks,
    
  46.   enableProfilerNestedUpdatePhase,
    
  47.   enableSchedulingProfiler,
    
  48.   enableSuspenseCallback,
    
  49.   enableScopeAPI,
    
  50.   enableUpdaterTracking,
    
  51.   enableCache,
    
  52.   enableTransitionTracing,
    
  53.   enableUseEffectEventHook,
    
  54.   enableFloat,
    
  55.   enableLegacyHidden,
    
  56.   enableHostSingletons,
    
  57.   alwaysThrottleRetries,
    
  58. } from 'shared/ReactFeatureFlags';
    
  59. import {
    
  60.   FunctionComponent,
    
  61.   ForwardRef,
    
  62.   ClassComponent,
    
  63.   HostRoot,
    
  64.   HostComponent,
    
  65.   HostHoistable,
    
  66.   HostSingleton,
    
  67.   HostText,
    
  68.   HostPortal,
    
  69.   Profiler,
    
  70.   SuspenseComponent,
    
  71.   DehydratedFragment,
    
  72.   IncompleteClassComponent,
    
  73.   MemoComponent,
    
  74.   SimpleMemoComponent,
    
  75.   SuspenseListComponent,
    
  76.   ScopeComponent,
    
  77.   OffscreenComponent,
    
  78.   LegacyHiddenComponent,
    
  79.   CacheComponent,
    
  80.   TracingMarkerComponent,
    
  81. } from './ReactWorkTags';
    
  82. import {
    
  83.   NoFlags,
    
  84.   ContentReset,
    
  85.   Placement,
    
  86.   ChildDeletion,
    
  87.   Snapshot,
    
  88.   Update,
    
  89.   Callback,
    
  90.   Ref,
    
  91.   Hydrating,
    
  92.   Passive,
    
  93.   BeforeMutationMask,
    
  94.   MutationMask,
    
  95.   LayoutMask,
    
  96.   PassiveMask,
    
  97.   Visibility,
    
  98.   ShouldSuspendCommit,
    
  99.   MaySuspendCommit,
    
  100. } from './ReactFiberFlags';
    
  101. import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
    
  102. import {
    
  103.   resetCurrentFiber as resetCurrentDebugFiberInDEV,
    
  104.   setCurrentFiber as setCurrentDebugFiberInDEV,
    
  105.   getCurrentFiber as getCurrentDebugFiberInDEV,
    
  106. } from './ReactCurrentFiber';
    
  107. import {resolveDefaultProps} from './ReactFiberLazyComponent';
    
  108. import {
    
  109.   isCurrentUpdateNested,
    
  110.   getCommitTime,
    
  111.   recordLayoutEffectDuration,
    
  112.   startLayoutEffectTimer,
    
  113.   recordPassiveEffectDuration,
    
  114.   startPassiveEffectTimer,
    
  115. } from './ReactProfilerTimer';
    
  116. import {ConcurrentMode, NoMode, ProfileMode} from './ReactTypeOfMode';
    
  117. import {
    
  118.   deferHiddenCallbacks,
    
  119.   commitHiddenCallbacks,
    
  120.   commitCallbacks,
    
  121. } from './ReactFiberClassUpdateQueue';
    
  122. import {
    
  123.   getPublicInstance,
    
  124.   supportsMutation,
    
  125.   supportsPersistence,
    
  126.   supportsHydration,
    
  127.   supportsResources,
    
  128.   supportsSingletons,
    
  129.   commitMount,
    
  130.   commitUpdate,
    
  131.   resetTextContent,
    
  132.   commitTextUpdate,
    
  133.   appendChild,
    
  134.   appendChildToContainer,
    
  135.   insertBefore,
    
  136.   insertInContainerBefore,
    
  137.   removeChild,
    
  138.   removeChildFromContainer,
    
  139.   clearSuspenseBoundary,
    
  140.   clearSuspenseBoundaryFromContainer,
    
  141.   replaceContainerChildren,
    
  142.   createContainerChildSet,
    
  143.   hideInstance,
    
  144.   hideTextInstance,
    
  145.   unhideInstance,
    
  146.   unhideTextInstance,
    
  147.   commitHydratedContainer,
    
  148.   commitHydratedSuspenseInstance,
    
  149.   clearContainer,
    
  150.   prepareScopeUpdate,
    
  151.   prepareForCommit,
    
  152.   beforeActiveInstanceBlur,
    
  153.   detachDeletedInstance,
    
  154.   clearSingleton,
    
  155.   acquireSingletonInstance,
    
  156.   releaseSingletonInstance,
    
  157.   getHoistableRoot,
    
  158.   acquireResource,
    
  159.   releaseResource,
    
  160.   hydrateHoistable,
    
  161.   mountHoistable,
    
  162.   unmountHoistable,
    
  163.   prepareToCommitHoistables,
    
  164.   suspendInstance,
    
  165.   suspendResource,
    
  166. } from './ReactFiberConfig';
    
  167. import {
    
  168.   captureCommitPhaseError,
    
  169.   resolveRetryWakeable,
    
  170.   markCommitTimeOfFallback,
    
  171.   enqueuePendingPassiveProfilerEffect,
    
  172.   restorePendingUpdaters,
    
  173.   addTransitionStartCallbackToPendingTransition,
    
  174.   addTransitionProgressCallbackToPendingTransition,
    
  175.   addTransitionCompleteCallbackToPendingTransition,
    
  176.   addMarkerProgressCallbackToPendingTransition,
    
  177.   addMarkerIncompleteCallbackToPendingTransition,
    
  178.   addMarkerCompleteCallbackToPendingTransition,
    
  179.   setIsRunningInsertionEffect,
    
  180.   getExecutionContext,
    
  181.   CommitContext,
    
  182.   NoContext,
    
  183. } from './ReactFiberWorkLoop';
    
  184. import {
    
  185.   NoFlags as NoHookEffect,
    
  186.   HasEffect as HookHasEffect,
    
  187.   Layout as HookLayout,
    
  188.   Insertion as HookInsertion,
    
  189.   Passive as HookPassive,
    
  190. } from './ReactHookEffectTags';
    
  191. import {didWarnAboutReassigningProps} from './ReactFiberBeginWork';
    
  192. import {doesFiberContain} from './ReactFiberTreeReflection';
    
  193. import {invokeGuardedCallback, clearCaughtError} from 'shared/ReactErrorUtils';
    
  194. import {
    
  195.   isDevToolsPresent,
    
  196.   markComponentPassiveEffectMountStarted,
    
  197.   markComponentPassiveEffectMountStopped,
    
  198.   markComponentPassiveEffectUnmountStarted,
    
  199.   markComponentPassiveEffectUnmountStopped,
    
  200.   markComponentLayoutEffectMountStarted,
    
  201.   markComponentLayoutEffectMountStopped,
    
  202.   markComponentLayoutEffectUnmountStarted,
    
  203.   markComponentLayoutEffectUnmountStopped,
    
  204.   onCommitUnmount,
    
  205. } from './ReactFiberDevToolsHook';
    
  206. import {releaseCache, retainCache} from './ReactFiberCacheComponent';
    
  207. import {clearTransitionsForLanes} from './ReactFiberLane';
    
  208. import {
    
  209.   OffscreenVisible,
    
  210.   OffscreenDetached,
    
  211.   OffscreenPassiveEffectsConnected,
    
  212. } from './ReactFiberActivityComponent';
    
  213. import {
    
  214.   TransitionRoot,
    
  215.   TransitionTracingMarker,
    
  216. } from './ReactFiberTracingMarkerComponent';
    
  217. import {scheduleUpdateOnFiber} from './ReactFiberWorkLoop';
    
  218. import {enqueueConcurrentRenderForLane} from './ReactFiberConcurrentUpdates';
    
  219. 
    
  220. let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;
    
  221. if (__DEV__) {
    
  222.   didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();
    
  223. }
    
  224. 
    
  225. // Used during the commit phase to track the state of the Offscreen component stack.
    
  226. // Allows us to avoid traversing the return path to find the nearest Offscreen ancestor.
    
  227. let offscreenSubtreeIsHidden: boolean = false;
    
  228. let offscreenSubtreeWasHidden: boolean = false;
    
  229. 
    
  230. const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
    
  231. 
    
  232. let nextEffect: Fiber | null = null;
    
  233. 
    
  234. // Used for Profiling builds to track updaters.
    
  235. let inProgressLanes: Lanes | null = null;
    
  236. let inProgressRoot: FiberRoot | null = null;
    
  237. 
    
  238. function shouldProfile(current: Fiber): boolean {
    
  239.   return (
    
  240.     enableProfilerTimer &&
    
  241.     enableProfilerCommitHooks &&
    
  242.     (current.mode & ProfileMode) !== NoMode &&
    
  243.     (getExecutionContext() & CommitContext) !== NoContext
    
  244.   );
    
  245. }
    
  246. 
    
  247. export function reportUncaughtErrorInDEV(error: mixed) {
    
  248.   // Wrapping each small part of the commit phase into a guarded
    
  249.   // callback is a bit too slow (https://github.com/facebook/react/pull/21666).
    
  250.   // But we rely on it to surface errors to DEV tools like overlays
    
  251.   // (https://github.com/facebook/react/issues/21712).
    
  252.   // As a compromise, rethrow only caught errors in a guard.
    
  253.   if (__DEV__) {
    
  254.     invokeGuardedCallback(null, () => {
    
  255.       throw error;
    
  256.     });
    
  257.     clearCaughtError();
    
  258.   }
    
  259. }
    
  260. 
    
  261. function callComponentWillUnmountWithTimer(current: Fiber, instance: any) {
    
  262.   instance.props = current.memoizedProps;
    
  263.   instance.state = current.memoizedState;
    
  264.   if (shouldProfile(current)) {
    
  265.     try {
    
  266.       startLayoutEffectTimer();
    
  267.       instance.componentWillUnmount();
    
  268.     } finally {
    
  269.       recordLayoutEffectDuration(current);
    
  270.     }
    
  271.   } else {
    
  272.     instance.componentWillUnmount();
    
  273.   }
    
  274. }
    
  275. 
    
  276. // Capture errors so they don't interrupt unmounting.
    
  277. function safelyCallComponentWillUnmount(
    
  278.   current: Fiber,
    
  279.   nearestMountedAncestor: Fiber | null,
    
  280.   instance: any,
    
  281. ) {
    
  282.   try {
    
  283.     callComponentWillUnmountWithTimer(current, instance);
    
  284.   } catch (error) {
    
  285.     captureCommitPhaseError(current, nearestMountedAncestor, error);
    
  286.   }
    
  287. }
    
  288. 
    
  289. // Capture errors so they don't interrupt mounting.
    
  290. function safelyAttachRef(current: Fiber, nearestMountedAncestor: Fiber | null) {
    
  291.   try {
    
  292.     commitAttachRef(current);
    
  293.   } catch (error) {
    
  294.     captureCommitPhaseError(current, nearestMountedAncestor, error);
    
  295.   }
    
  296. }
    
  297. 
    
  298. function safelyDetachRef(current: Fiber, nearestMountedAncestor: Fiber | null) {
    
  299.   const ref = current.ref;
    
  300.   const refCleanup = current.refCleanup;
    
  301. 
    
  302.   if (ref !== null) {
    
  303.     if (typeof refCleanup === 'function') {
    
  304.       try {
    
  305.         if (shouldProfile(current)) {
    
  306.           try {
    
  307.             startLayoutEffectTimer();
    
  308.             refCleanup();
    
  309.           } finally {
    
  310.             recordLayoutEffectDuration(current);
    
  311.           }
    
  312.         } else {
    
  313.           refCleanup();
    
  314.         }
    
  315.       } catch (error) {
    
  316.         captureCommitPhaseError(current, nearestMountedAncestor, error);
    
  317.       } finally {
    
  318.         // `refCleanup` has been called. Nullify all references to it to prevent double invocation.
    
  319.         current.refCleanup = null;
    
  320.         const finishedWork = current.alternate;
    
  321.         if (finishedWork != null) {
    
  322.           finishedWork.refCleanup = null;
    
  323.         }
    
  324.       }
    
  325.     } else if (typeof ref === 'function') {
    
  326.       let retVal;
    
  327.       try {
    
  328.         if (shouldProfile(current)) {
    
  329.           try {
    
  330.             startLayoutEffectTimer();
    
  331.             retVal = ref(null);
    
  332.           } finally {
    
  333.             recordLayoutEffectDuration(current);
    
  334.           }
    
  335.         } else {
    
  336.           retVal = ref(null);
    
  337.         }
    
  338.       } catch (error) {
    
  339.         captureCommitPhaseError(current, nearestMountedAncestor, error);
    
  340.       }
    
  341.       if (__DEV__) {
    
  342.         if (typeof retVal === 'function') {
    
  343.           console.error(
    
  344.             'Unexpected return value from a callback ref in %s. ' +
    
  345.               'A callback ref should not return a function.',
    
  346.             getComponentNameFromFiber(current),
    
  347.           );
    
  348.         }
    
  349.       }
    
  350.     } else {
    
  351.       // $FlowFixMe[incompatible-use] unable to narrow type to RefObject
    
  352.       ref.current = null;
    
  353.     }
    
  354.   }
    
  355. }
    
  356. 
    
  357. function safelyCallDestroy(
    
  358.   current: Fiber,
    
  359.   nearestMountedAncestor: Fiber | null,
    
  360.   destroy: () => void,
    
  361. ) {
    
  362.   try {
    
  363.     destroy();
    
  364.   } catch (error) {
    
  365.     captureCommitPhaseError(current, nearestMountedAncestor, error);
    
  366.   }
    
  367. }
    
  368. 
    
  369. let focusedInstanceHandle: null | Fiber = null;
    
  370. let shouldFireAfterActiveInstanceBlur: boolean = false;
    
  371. 
    
  372. export function commitBeforeMutationEffects(
    
  373.   root: FiberRoot,
    
  374.   firstChild: Fiber,
    
  375. ): boolean {
    
  376.   focusedInstanceHandle = prepareForCommit(root.containerInfo);
    
  377. 
    
  378.   nextEffect = firstChild;
    
  379.   commitBeforeMutationEffects_begin();
    
  380. 
    
  381.   // We no longer need to track the active instance fiber
    
  382.   const shouldFire = shouldFireAfterActiveInstanceBlur;
    
  383.   shouldFireAfterActiveInstanceBlur = false;
    
  384.   focusedInstanceHandle = null;
    
  385. 
    
  386.   return shouldFire;
    
  387. }
    
  388. 
    
  389. function commitBeforeMutationEffects_begin() {
    
  390.   while (nextEffect !== null) {
    
  391.     const fiber = nextEffect;
    
  392. 
    
  393.     // This phase is only used for beforeActiveInstanceBlur.
    
  394.     // Let's skip the whole loop if it's off.
    
  395.     if (enableCreateEventHandleAPI) {
    
  396.       // TODO: Should wrap this in flags check, too, as optimization
    
  397.       const deletions = fiber.deletions;
    
  398.       if (deletions !== null) {
    
  399.         for (let i = 0; i < deletions.length; i++) {
    
  400.           const deletion = deletions[i];
    
  401.           commitBeforeMutationEffectsDeletion(deletion);
    
  402.         }
    
  403.       }
    
  404.     }
    
  405. 
    
  406.     const child = fiber.child;
    
  407.     if (
    
  408.       (fiber.subtreeFlags & BeforeMutationMask) !== NoFlags &&
    
  409.       child !== null
    
  410.     ) {
    
  411.       child.return = fiber;
    
  412.       nextEffect = child;
    
  413.     } else {
    
  414.       commitBeforeMutationEffects_complete();
    
  415.     }
    
  416.   }
    
  417. }
    
  418. 
    
  419. function commitBeforeMutationEffects_complete() {
    
  420.   while (nextEffect !== null) {
    
  421.     const fiber = nextEffect;
    
  422.     setCurrentDebugFiberInDEV(fiber);
    
  423.     try {
    
  424.       commitBeforeMutationEffectsOnFiber(fiber);
    
  425.     } catch (error) {
    
  426.       captureCommitPhaseError(fiber, fiber.return, error);
    
  427.     }
    
  428.     resetCurrentDebugFiberInDEV();
    
  429. 
    
  430.     const sibling = fiber.sibling;
    
  431.     if (sibling !== null) {
    
  432.       sibling.return = fiber.return;
    
  433.       nextEffect = sibling;
    
  434.       return;
    
  435.     }
    
  436. 
    
  437.     nextEffect = fiber.return;
    
  438.   }
    
  439. }
    
  440. 
    
  441. function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
    
  442.   const current = finishedWork.alternate;
    
  443.   const flags = finishedWork.flags;
    
  444. 
    
  445.   if (enableCreateEventHandleAPI) {
    
  446.     if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
    
  447.       // Check to see if the focused element was inside of a hidden (Suspense) subtree.
    
  448.       // TODO: Move this out of the hot path using a dedicated effect tag.
    
  449.       if (
    
  450.         finishedWork.tag === SuspenseComponent &&
    
  451.         isSuspenseBoundaryBeingHidden(current, finishedWork) &&
    
  452.         // $FlowFixMe[incompatible-call] found when upgrading Flow
    
  453.         doesFiberContain(finishedWork, focusedInstanceHandle)
    
  454.       ) {
    
  455.         shouldFireAfterActiveInstanceBlur = true;
    
  456.         beforeActiveInstanceBlur(finishedWork);
    
  457.       }
    
  458.     }
    
  459.   }
    
  460. 
    
  461.   if ((flags & Snapshot) !== NoFlags) {
    
  462.     setCurrentDebugFiberInDEV(finishedWork);
    
  463.   }
    
  464. 
    
  465.   switch (finishedWork.tag) {
    
  466.     case FunctionComponent: {
    
  467.       if (enableUseEffectEventHook) {
    
  468.         if ((flags & Update) !== NoFlags) {
    
  469.           commitUseEffectEventMount(finishedWork);
    
  470.         }
    
  471.       }
    
  472.       break;
    
  473.     }
    
  474.     case ForwardRef:
    
  475.     case SimpleMemoComponent: {
    
  476.       break;
    
  477.     }
    
  478.     case ClassComponent: {
    
  479.       if ((flags & Snapshot) !== NoFlags) {
    
  480.         if (current !== null) {
    
  481.           const prevProps = current.memoizedProps;
    
  482.           const prevState = current.memoizedState;
    
  483.           const instance = finishedWork.stateNode;
    
  484.           // We could update instance props and state here,
    
  485.           // but instead we rely on them being set during last render.
    
  486.           // TODO: revisit this when we implement resuming.
    
  487.           if (__DEV__) {
    
  488.             if (
    
  489.               finishedWork.type === finishedWork.elementType &&
    
  490.               !didWarnAboutReassigningProps
    
  491.             ) {
    
  492.               if (instance.props !== finishedWork.memoizedProps) {
    
  493.                 console.error(
    
  494.                   'Expected %s props to match memoized props before ' +
    
  495.                     'getSnapshotBeforeUpdate. ' +
    
  496.                     'This might either be because of a bug in React, or because ' +
    
  497.                     'a component reassigns its own `this.props`. ' +
    
  498.                     'Please file an issue.',
    
  499.                   getComponentNameFromFiber(finishedWork) || 'instance',
    
  500.                 );
    
  501.               }
    
  502.               if (instance.state !== finishedWork.memoizedState) {
    
  503.                 console.error(
    
  504.                   'Expected %s state to match memoized state before ' +
    
  505.                     'getSnapshotBeforeUpdate. ' +
    
  506.                     'This might either be because of a bug in React, or because ' +
    
  507.                     'a component reassigns its own `this.state`. ' +
    
  508.                     'Please file an issue.',
    
  509.                   getComponentNameFromFiber(finishedWork) || 'instance',
    
  510.                 );
    
  511.               }
    
  512.             }
    
  513.           }
    
  514.           const snapshot = instance.getSnapshotBeforeUpdate(
    
  515.             finishedWork.elementType === finishedWork.type
    
  516.               ? prevProps
    
  517.               : resolveDefaultProps(finishedWork.type, prevProps),
    
  518.             prevState,
    
  519.           );
    
  520.           if (__DEV__) {
    
  521.             const didWarnSet =
    
  522.               ((didWarnAboutUndefinedSnapshotBeforeUpdate: any): Set<mixed>);
    
  523.             if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {
    
  524.               didWarnSet.add(finishedWork.type);
    
  525.               console.error(
    
  526.                 '%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' +
    
  527.                   'must be returned. You have returned undefined.',
    
  528.                 getComponentNameFromFiber(finishedWork),
    
  529.               );
    
  530.             }
    
  531.           }
    
  532.           instance.__reactInternalSnapshotBeforeUpdate = snapshot;
    
  533.         }
    
  534.       }
    
  535.       break;
    
  536.     }
    
  537.     case HostRoot: {
    
  538.       if ((flags & Snapshot) !== NoFlags) {
    
  539.         if (supportsMutation) {
    
  540.           const root = finishedWork.stateNode;
    
  541.           clearContainer(root.containerInfo);
    
  542.         }
    
  543.       }
    
  544.       break;
    
  545.     }
    
  546.     case HostComponent:
    
  547.     case HostHoistable:
    
  548.     case HostSingleton:
    
  549.     case HostText:
    
  550.     case HostPortal:
    
  551.     case IncompleteClassComponent:
    
  552.       // Nothing to do for these component types
    
  553.       break;
    
  554.     default: {
    
  555.       if ((flags & Snapshot) !== NoFlags) {
    
  556.         throw new Error(
    
  557.           'This unit of work tag should not have side-effects. This error is ' +
    
  558.             'likely caused by a bug in React. Please file an issue.',
    
  559.         );
    
  560.       }
    
  561.     }
    
  562.   }
    
  563. 
    
  564.   if ((flags & Snapshot) !== NoFlags) {
    
  565.     resetCurrentDebugFiberInDEV();
    
  566.   }
    
  567. }
    
  568. 
    
  569. function commitBeforeMutationEffectsDeletion(deletion: Fiber) {
    
  570.   if (enableCreateEventHandleAPI) {
    
  571.     // TODO (effects) It would be nice to avoid calling doesFiberContain()
    
  572.     // Maybe we can repurpose one of the subtreeFlags positions for this instead?
    
  573.     // Use it to store which part of the tree the focused instance is in?
    
  574.     // This assumes we can safely determine that instance during the "render" phase.
    
  575.     if (doesFiberContain(deletion, ((focusedInstanceHandle: any): Fiber))) {
    
  576.       shouldFireAfterActiveInstanceBlur = true;
    
  577.       beforeActiveInstanceBlur(deletion);
    
  578.     }
    
  579.   }
    
  580. }
    
  581. 
    
  582. function commitHookEffectListUnmount(
    
  583.   flags: HookFlags,
    
  584.   finishedWork: Fiber,
    
  585.   nearestMountedAncestor: Fiber | null,
    
  586. ) {
    
  587.   const updateQueue: FunctionComponentUpdateQueue | null =
    
  588.     (finishedWork.updateQueue: any);
    
  589.   const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
    
  590.   if (lastEffect !== null) {
    
  591.     const firstEffect = lastEffect.next;
    
  592.     let effect = firstEffect;
    
  593.     do {
    
  594.       if ((effect.tag & flags) === flags) {
    
  595.         // Unmount
    
  596.         const inst = effect.inst;
    
  597.         const destroy = inst.destroy;
    
  598.         if (destroy !== undefined) {
    
  599.           inst.destroy = undefined;
    
  600.           if (enableSchedulingProfiler) {
    
  601.             if ((flags & HookPassive) !== NoHookEffect) {
    
  602.               markComponentPassiveEffectUnmountStarted(finishedWork);
    
  603.             } else if ((flags & HookLayout) !== NoHookEffect) {
    
  604.               markComponentLayoutEffectUnmountStarted(finishedWork);
    
  605.             }
    
  606.           }
    
  607. 
    
  608.           if (__DEV__) {
    
  609.             if ((flags & HookInsertion) !== NoHookEffect) {
    
  610.               setIsRunningInsertionEffect(true);
    
  611.             }
    
  612.           }
    
  613.           safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);
    
  614.           if (__DEV__) {
    
  615.             if ((flags & HookInsertion) !== NoHookEffect) {
    
  616.               setIsRunningInsertionEffect(false);
    
  617.             }
    
  618.           }
    
  619. 
    
  620.           if (enableSchedulingProfiler) {
    
  621.             if ((flags & HookPassive) !== NoHookEffect) {
    
  622.               markComponentPassiveEffectUnmountStopped();
    
  623.             } else if ((flags & HookLayout) !== NoHookEffect) {
    
  624.               markComponentLayoutEffectUnmountStopped();
    
  625.             }
    
  626.           }
    
  627.         }
    
  628.       }
    
  629.       effect = effect.next;
    
  630.     } while (effect !== firstEffect);
    
  631.   }
    
  632. }
    
  633. 
    
  634. function commitHookEffectListMount(flags: HookFlags, finishedWork: Fiber) {
    
  635.   const updateQueue: FunctionComponentUpdateQueue | null =
    
  636.     (finishedWork.updateQueue: any);
    
  637.   const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
    
  638.   if (lastEffect !== null) {
    
  639.     const firstEffect = lastEffect.next;
    
  640.     let effect = firstEffect;
    
  641.     do {
    
  642.       if ((effect.tag & flags) === flags) {
    
  643.         if (enableSchedulingProfiler) {
    
  644.           if ((flags & HookPassive) !== NoHookEffect) {
    
  645.             markComponentPassiveEffectMountStarted(finishedWork);
    
  646.           } else if ((flags & HookLayout) !== NoHookEffect) {
    
  647.             markComponentLayoutEffectMountStarted(finishedWork);
    
  648.           }
    
  649.         }
    
  650. 
    
  651.         // Mount
    
  652.         const create = effect.create;
    
  653.         if (__DEV__) {
    
  654.           if ((flags & HookInsertion) !== NoHookEffect) {
    
  655.             setIsRunningInsertionEffect(true);
    
  656.           }
    
  657.         }
    
  658.         const inst = effect.inst;
    
  659.         const destroy = create();
    
  660.         inst.destroy = destroy;
    
  661.         if (__DEV__) {
    
  662.           if ((flags & HookInsertion) !== NoHookEffect) {
    
  663.             setIsRunningInsertionEffect(false);
    
  664.           }
    
  665.         }
    
  666. 
    
  667.         if (enableSchedulingProfiler) {
    
  668.           if ((flags & HookPassive) !== NoHookEffect) {
    
  669.             markComponentPassiveEffectMountStopped();
    
  670.           } else if ((flags & HookLayout) !== NoHookEffect) {
    
  671.             markComponentLayoutEffectMountStopped();
    
  672.           }
    
  673.         }
    
  674. 
    
  675.         if (__DEV__) {
    
  676.           if (destroy !== undefined && typeof destroy !== 'function') {
    
  677.             let hookName;
    
  678.             if ((effect.tag & HookLayout) !== NoFlags) {
    
  679.               hookName = 'useLayoutEffect';
    
  680.             } else if ((effect.tag & HookInsertion) !== NoFlags) {
    
  681.               hookName = 'useInsertionEffect';
    
  682.             } else {
    
  683.               hookName = 'useEffect';
    
  684.             }
    
  685.             let addendum;
    
  686.             if (destroy === null) {
    
  687.               addendum =
    
  688.                 ' You returned null. If your effect does not require clean ' +
    
  689.                 'up, return undefined (or nothing).';
    
  690.             } else if (typeof destroy.then === 'function') {
    
  691.               addendum =
    
  692.                 '\n\nIt looks like you wrote ' +
    
  693.                 hookName +
    
  694.                 '(async () => ...) or returned a Promise. ' +
    
  695.                 'Instead, write the async function inside your effect ' +
    
  696.                 'and call it immediately:\n\n' +
    
  697.                 hookName +
    
  698.                 '(() => {\n' +
    
  699.                 '  async function fetchData() {\n' +
    
  700.                 '    // You can await here\n' +
    
  701.                 '    const response = await MyAPI.getData(someId);\n' +
    
  702.                 '    // ...\n' +
    
  703.                 '  }\n' +
    
  704.                 '  fetchData();\n' +
    
  705.                 `}, [someId]); // Or [] if effect doesn't need props or state\n\n` +
    
  706.                 'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching';
    
  707.             } else {
    
  708.               addendum = ' You returned: ' + destroy;
    
  709.             }
    
  710.             console.error(
    
  711.               '%s must not return anything besides a function, ' +
    
  712.                 'which is used for clean-up.%s',
    
  713.               hookName,
    
  714.               addendum,
    
  715.             );
    
  716.           }
    
  717.         }
    
  718.       }
    
  719.       effect = effect.next;
    
  720.     } while (effect !== firstEffect);
    
  721.   }
    
  722. }
    
  723. 
    
  724. function commitUseEffectEventMount(finishedWork: Fiber) {
    
  725.   const updateQueue: FunctionComponentUpdateQueue | null =
    
  726.     (finishedWork.updateQueue: any);
    
  727.   const eventPayloads = updateQueue !== null ? updateQueue.events : null;
    
  728.   if (eventPayloads !== null) {
    
  729.     for (let ii = 0; ii < eventPayloads.length; ii++) {
    
  730.       const {ref, nextImpl} = eventPayloads[ii];
    
  731.       ref.impl = nextImpl;
    
  732.     }
    
  733.   }
    
  734. }
    
  735. 
    
  736. export function commitPassiveEffectDurations(
    
  737.   finishedRoot: FiberRoot,
    
  738.   finishedWork: Fiber,
    
  739. ): void {
    
  740.   if (
    
  741.     enableProfilerTimer &&
    
  742.     enableProfilerCommitHooks &&
    
  743.     getExecutionContext() & CommitContext
    
  744.   ) {
    
  745.     // Only Profilers with work in their subtree will have an Update effect scheduled.
    
  746.     if ((finishedWork.flags & Update) !== NoFlags) {
    
  747.       switch (finishedWork.tag) {
    
  748.         case Profiler: {
    
  749.           const {passiveEffectDuration} = finishedWork.stateNode;
    
  750.           const {id, onPostCommit} = finishedWork.memoizedProps;
    
  751. 
    
  752.           // This value will still reflect the previous commit phase.
    
  753.           // It does not get reset until the start of the next commit phase.
    
  754.           const commitTime = getCommitTime();
    
  755. 
    
  756.           let phase = finishedWork.alternate === null ? 'mount' : 'update';
    
  757.           if (enableProfilerNestedUpdatePhase) {
    
  758.             if (isCurrentUpdateNested()) {
    
  759.               phase = 'nested-update';
    
  760.             }
    
  761.           }
    
  762. 
    
  763.           if (typeof onPostCommit === 'function') {
    
  764.             onPostCommit(id, phase, passiveEffectDuration, commitTime);
    
  765.           }
    
  766. 
    
  767.           // Bubble times to the next nearest ancestor Profiler.
    
  768.           // After we process that Profiler, we'll bubble further up.
    
  769.           let parentFiber = finishedWork.return;
    
  770.           outer: while (parentFiber !== null) {
    
  771.             switch (parentFiber.tag) {
    
  772.               case HostRoot:
    
  773.                 const root = parentFiber.stateNode;
    
  774.                 root.passiveEffectDuration += passiveEffectDuration;
    
  775.                 break outer;
    
  776.               case Profiler:
    
  777.                 const parentStateNode = parentFiber.stateNode;
    
  778.                 parentStateNode.passiveEffectDuration += passiveEffectDuration;
    
  779.                 break outer;
    
  780.             }
    
  781.             parentFiber = parentFiber.return;
    
  782.           }
    
  783.           break;
    
  784.         }
    
  785.         default:
    
  786.           break;
    
  787.       }
    
  788.     }
    
  789.   }
    
  790. }
    
  791. 
    
  792. function commitHookLayoutEffects(finishedWork: Fiber, hookFlags: HookFlags) {
    
  793.   // At this point layout effects have already been destroyed (during mutation phase).
    
  794.   // This is done to prevent sibling component effects from interfering with each other,
    
  795.   // e.g. a destroy function in one component should never override a ref set
    
  796.   // by a create function in another component during the same commit.
    
  797.   if (shouldProfile(finishedWork)) {
    
  798.     try {
    
  799.       startLayoutEffectTimer();
    
  800.       commitHookEffectListMount(hookFlags, finishedWork);
    
  801.     } catch (error) {
    
  802.       captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  803.     }
    
  804.     recordLayoutEffectDuration(finishedWork);
    
  805.   } else {
    
  806.     try {
    
  807.       commitHookEffectListMount(hookFlags, finishedWork);
    
  808.     } catch (error) {
    
  809.       captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  810.     }
    
  811.   }
    
  812. }
    
  813. 
    
  814. function commitClassLayoutLifecycles(
    
  815.   finishedWork: Fiber,
    
  816.   current: Fiber | null,
    
  817. ) {
    
  818.   const instance = finishedWork.stateNode;
    
  819.   if (current === null) {
    
  820.     // We could update instance props and state here,
    
  821.     // but instead we rely on them being set during last render.
    
  822.     // TODO: revisit this when we implement resuming.
    
  823.     if (__DEV__) {
    
  824.       if (
    
  825.         finishedWork.type === finishedWork.elementType &&
    
  826.         !didWarnAboutReassigningProps
    
  827.       ) {
    
  828.         if (instance.props !== finishedWork.memoizedProps) {
    
  829.           console.error(
    
  830.             'Expected %s props to match memoized props before ' +
    
  831.               'componentDidMount. ' +
    
  832.               'This might either be because of a bug in React, or because ' +
    
  833.               'a component reassigns its own `this.props`. ' +
    
  834.               'Please file an issue.',
    
  835.             getComponentNameFromFiber(finishedWork) || 'instance',
    
  836.           );
    
  837.         }
    
  838.         if (instance.state !== finishedWork.memoizedState) {
    
  839.           console.error(
    
  840.             'Expected %s state to match memoized state before ' +
    
  841.               'componentDidMount. ' +
    
  842.               'This might either be because of a bug in React, or because ' +
    
  843.               'a component reassigns its own `this.state`. ' +
    
  844.               'Please file an issue.',
    
  845.             getComponentNameFromFiber(finishedWork) || 'instance',
    
  846.           );
    
  847.         }
    
  848.       }
    
  849.     }
    
  850.     if (shouldProfile(finishedWork)) {
    
  851.       try {
    
  852.         startLayoutEffectTimer();
    
  853.         instance.componentDidMount();
    
  854.       } catch (error) {
    
  855.         captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  856.       }
    
  857.       recordLayoutEffectDuration(finishedWork);
    
  858.     } else {
    
  859.       try {
    
  860.         instance.componentDidMount();
    
  861.       } catch (error) {
    
  862.         captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  863.       }
    
  864.     }
    
  865.   } else {
    
  866.     const prevProps =
    
  867.       finishedWork.elementType === finishedWork.type
    
  868.         ? current.memoizedProps
    
  869.         : resolveDefaultProps(finishedWork.type, current.memoizedProps);
    
  870.     const prevState = current.memoizedState;
    
  871.     // We could update instance props and state here,
    
  872.     // but instead we rely on them being set during last render.
    
  873.     // TODO: revisit this when we implement resuming.
    
  874.     if (__DEV__) {
    
  875.       if (
    
  876.         finishedWork.type === finishedWork.elementType &&
    
  877.         !didWarnAboutReassigningProps
    
  878.       ) {
    
  879.         if (instance.props !== finishedWork.memoizedProps) {
    
  880.           console.error(
    
  881.             'Expected %s props to match memoized props before ' +
    
  882.               'componentDidUpdate. ' +
    
  883.               'This might either be because of a bug in React, or because ' +
    
  884.               'a component reassigns its own `this.props`. ' +
    
  885.               'Please file an issue.',
    
  886.             getComponentNameFromFiber(finishedWork) || 'instance',
    
  887.           );
    
  888.         }
    
  889.         if (instance.state !== finishedWork.memoizedState) {
    
  890.           console.error(
    
  891.             'Expected %s state to match memoized state before ' +
    
  892.               'componentDidUpdate. ' +
    
  893.               'This might either be because of a bug in React, or because ' +
    
  894.               'a component reassigns its own `this.state`. ' +
    
  895.               'Please file an issue.',
    
  896.             getComponentNameFromFiber(finishedWork) || 'instance',
    
  897.           );
    
  898.         }
    
  899.       }
    
  900.     }
    
  901.     if (shouldProfile(finishedWork)) {
    
  902.       try {
    
  903.         startLayoutEffectTimer();
    
  904.         instance.componentDidUpdate(
    
  905.           prevProps,
    
  906.           prevState,
    
  907.           instance.__reactInternalSnapshotBeforeUpdate,
    
  908.         );
    
  909.       } catch (error) {
    
  910.         captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  911.       }
    
  912.       recordLayoutEffectDuration(finishedWork);
    
  913.     } else {
    
  914.       try {
    
  915.         instance.componentDidUpdate(
    
  916.           prevProps,
    
  917.           prevState,
    
  918.           instance.__reactInternalSnapshotBeforeUpdate,
    
  919.         );
    
  920.       } catch (error) {
    
  921.         captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  922.       }
    
  923.     }
    
  924.   }
    
  925. }
    
  926. 
    
  927. function commitClassCallbacks(finishedWork: Fiber) {
    
  928.   // TODO: I think this is now always non-null by the time it reaches the
    
  929.   // commit phase. Consider removing the type check.
    
  930.   const updateQueue: UpdateQueue<mixed> | null =
    
  931.     (finishedWork.updateQueue: any);
    
  932.   if (updateQueue !== null) {
    
  933.     const instance = finishedWork.stateNode;
    
  934.     if (__DEV__) {
    
  935.       if (
    
  936.         finishedWork.type === finishedWork.elementType &&
    
  937.         !didWarnAboutReassigningProps
    
  938.       ) {
    
  939.         if (instance.props !== finishedWork.memoizedProps) {
    
  940.           console.error(
    
  941.             'Expected %s props to match memoized props before ' +
    
  942.               'processing the update queue. ' +
    
  943.               'This might either be because of a bug in React, or because ' +
    
  944.               'a component reassigns its own `this.props`. ' +
    
  945.               'Please file an issue.',
    
  946.             getComponentNameFromFiber(finishedWork) || 'instance',
    
  947.           );
    
  948.         }
    
  949.         if (instance.state !== finishedWork.memoizedState) {
    
  950.           console.error(
    
  951.             'Expected %s state to match memoized state before ' +
    
  952.               'processing the update queue. ' +
    
  953.               'This might either be because of a bug in React, or because ' +
    
  954.               'a component reassigns its own `this.state`. ' +
    
  955.               'Please file an issue.',
    
  956.             getComponentNameFromFiber(finishedWork) || 'instance',
    
  957.           );
    
  958.         }
    
  959.       }
    
  960.     }
    
  961.     // We could update instance props and state here,
    
  962.     // but instead we rely on them being set during last render.
    
  963.     // TODO: revisit this when we implement resuming.
    
  964.     try {
    
  965.       commitCallbacks(updateQueue, instance);
    
  966.     } catch (error) {
    
  967.       captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  968.     }
    
  969.   }
    
  970. }
    
  971. 
    
  972. function commitHostComponentMount(finishedWork: Fiber) {
    
  973.   const type = finishedWork.type;
    
  974.   const props = finishedWork.memoizedProps;
    
  975.   const instance: Instance = finishedWork.stateNode;
    
  976.   try {
    
  977.     commitMount(instance, type, props, finishedWork);
    
  978.   } catch (error) {
    
  979.     captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  980.   }
    
  981. }
    
  982. 
    
  983. function commitProfilerUpdate(finishedWork: Fiber, current: Fiber | null) {
    
  984.   if (enableProfilerTimer && getExecutionContext() & CommitContext) {
    
  985.     try {
    
  986.       const {onCommit, onRender} = finishedWork.memoizedProps;
    
  987.       const {effectDuration} = finishedWork.stateNode;
    
  988. 
    
  989.       const commitTime = getCommitTime();
    
  990. 
    
  991.       let phase = current === null ? 'mount' : 'update';
    
  992.       if (enableProfilerNestedUpdatePhase) {
    
  993.         if (isCurrentUpdateNested()) {
    
  994.           phase = 'nested-update';
    
  995.         }
    
  996.       }
    
  997. 
    
  998.       if (typeof onRender === 'function') {
    
  999.         onRender(
    
  1000.           finishedWork.memoizedProps.id,
    
  1001.           phase,
    
  1002.           finishedWork.actualDuration,
    
  1003.           finishedWork.treeBaseDuration,
    
  1004.           finishedWork.actualStartTime,
    
  1005.           commitTime,
    
  1006.         );
    
  1007.       }
    
  1008. 
    
  1009.       if (enableProfilerCommitHooks) {
    
  1010.         if (typeof onCommit === 'function') {
    
  1011.           onCommit(
    
  1012.             finishedWork.memoizedProps.id,
    
  1013.             phase,
    
  1014.             effectDuration,
    
  1015.             commitTime,
    
  1016.           );
    
  1017.         }
    
  1018. 
    
  1019.         // Schedule a passive effect for this Profiler to call onPostCommit hooks.
    
  1020.         // This effect should be scheduled even if there is no onPostCommit callback for this Profiler,
    
  1021.         // because the effect is also where times bubble to parent Profilers.
    
  1022.         enqueuePendingPassiveProfilerEffect(finishedWork);
    
  1023. 
    
  1024.         // Propagate layout effect durations to the next nearest Profiler ancestor.
    
  1025.         // Do not reset these values until the next render so DevTools has a chance to read them first.
    
  1026.         let parentFiber = finishedWork.return;
    
  1027.         outer: while (parentFiber !== null) {
    
  1028.           switch (parentFiber.tag) {
    
  1029.             case HostRoot:
    
  1030.               const root = parentFiber.stateNode;
    
  1031.               root.effectDuration += effectDuration;
    
  1032.               break outer;
    
  1033.             case Profiler:
    
  1034.               const parentStateNode = parentFiber.stateNode;
    
  1035.               parentStateNode.effectDuration += effectDuration;
    
  1036.               break outer;
    
  1037.           }
    
  1038.           parentFiber = parentFiber.return;
    
  1039.         }
    
  1040.       }
    
  1041.     } catch (error) {
    
  1042.       captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  1043.     }
    
  1044.   }
    
  1045. }
    
  1046. 
    
  1047. function commitLayoutEffectOnFiber(
    
  1048.   finishedRoot: FiberRoot,
    
  1049.   current: Fiber | null,
    
  1050.   finishedWork: Fiber,
    
  1051.   committedLanes: Lanes,
    
  1052. ): void {
    
  1053.   // When updating this function, also update reappearLayoutEffects, which does
    
  1054.   // most of the same things when an offscreen tree goes from hidden -> visible.
    
  1055.   const flags = finishedWork.flags;
    
  1056.   switch (finishedWork.tag) {
    
  1057.     case FunctionComponent:
    
  1058.     case ForwardRef:
    
  1059.     case SimpleMemoComponent: {
    
  1060.       recursivelyTraverseLayoutEffects(
    
  1061.         finishedRoot,
    
  1062.         finishedWork,
    
  1063.         committedLanes,
    
  1064.       );
    
  1065.       if (flags & Update) {
    
  1066.         commitHookLayoutEffects(finishedWork, HookLayout | HookHasEffect);
    
  1067.       }
    
  1068.       break;
    
  1069.     }
    
  1070.     case ClassComponent: {
    
  1071.       recursivelyTraverseLayoutEffects(
    
  1072.         finishedRoot,
    
  1073.         finishedWork,
    
  1074.         committedLanes,
    
  1075.       );
    
  1076.       if (flags & Update) {
    
  1077.         commitClassLayoutLifecycles(finishedWork, current);
    
  1078.       }
    
  1079. 
    
  1080.       if (flags & Callback) {
    
  1081.         commitClassCallbacks(finishedWork);
    
  1082.       }
    
  1083. 
    
  1084.       if (flags & Ref) {
    
  1085.         safelyAttachRef(finishedWork, finishedWork.return);
    
  1086.       }
    
  1087.       break;
    
  1088.     }
    
  1089.     case HostRoot: {
    
  1090.       recursivelyTraverseLayoutEffects(
    
  1091.         finishedRoot,
    
  1092.         finishedWork,
    
  1093.         committedLanes,
    
  1094.       );
    
  1095.       if (flags & Callback) {
    
  1096.         // TODO: I think this is now always non-null by the time it reaches the
    
  1097.         // commit phase. Consider removing the type check.
    
  1098.         const updateQueue: UpdateQueue<mixed> | null =
    
  1099.           (finishedWork.updateQueue: any);
    
  1100.         if (updateQueue !== null) {
    
  1101.           let instance = null;
    
  1102.           if (finishedWork.child !== null) {
    
  1103.             switch (finishedWork.child.tag) {
    
  1104.               case HostSingleton:
    
  1105.               case HostComponent:
    
  1106.                 instance = getPublicInstance(finishedWork.child.stateNode);
    
  1107.                 break;
    
  1108.               case ClassComponent:
    
  1109.                 instance = finishedWork.child.stateNode;
    
  1110.                 break;
    
  1111.             }
    
  1112.           }
    
  1113.           try {
    
  1114.             commitCallbacks(updateQueue, instance);
    
  1115.           } catch (error) {
    
  1116.             captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  1117.           }
    
  1118.         }
    
  1119.       }
    
  1120.       break;
    
  1121.     }
    
  1122.     case HostHoistable: {
    
  1123.       if (enableFloat && supportsResources) {
    
  1124.         recursivelyTraverseLayoutEffects(
    
  1125.           finishedRoot,
    
  1126.           finishedWork,
    
  1127.           committedLanes,
    
  1128.         );
    
  1129. 
    
  1130.         if (flags & Ref) {
    
  1131.           safelyAttachRef(finishedWork, finishedWork.return);
    
  1132.         }
    
  1133.         break;
    
  1134.       }
    
  1135.       // Fall through
    
  1136.     }
    
  1137.     case HostSingleton:
    
  1138.     case HostComponent: {
    
  1139.       recursivelyTraverseLayoutEffects(
    
  1140.         finishedRoot,
    
  1141.         finishedWork,
    
  1142.         committedLanes,
    
  1143.       );
    
  1144. 
    
  1145.       // Renderers may schedule work to be done after host components are mounted
    
  1146.       // (eg DOM renderer may schedule auto-focus for inputs and form controls).
    
  1147.       // These effects should only be committed when components are first mounted,
    
  1148.       // aka when there is no current/alternate.
    
  1149.       if (current === null && flags & Update) {
    
  1150.         commitHostComponentMount(finishedWork);
    
  1151.       }
    
  1152. 
    
  1153.       if (flags & Ref) {
    
  1154.         safelyAttachRef(finishedWork, finishedWork.return);
    
  1155.       }
    
  1156.       break;
    
  1157.     }
    
  1158.     case Profiler: {
    
  1159.       recursivelyTraverseLayoutEffects(
    
  1160.         finishedRoot,
    
  1161.         finishedWork,
    
  1162.         committedLanes,
    
  1163.       );
    
  1164.       // TODO: Should this fire inside an offscreen tree? Or should it wait to
    
  1165.       // fire when the tree becomes visible again.
    
  1166.       if (flags & Update) {
    
  1167.         commitProfilerUpdate(finishedWork, current);
    
  1168.       }
    
  1169.       break;
    
  1170.     }
    
  1171.     case SuspenseComponent: {
    
  1172.       recursivelyTraverseLayoutEffects(
    
  1173.         finishedRoot,
    
  1174.         finishedWork,
    
  1175.         committedLanes,
    
  1176.       );
    
  1177.       if (flags & Update) {
    
  1178.         commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
    
  1179.       }
    
  1180.       break;
    
  1181.     }
    
  1182.     case OffscreenComponent: {
    
  1183.       const isModernRoot = (finishedWork.mode & ConcurrentMode) !== NoMode;
    
  1184.       if (isModernRoot) {
    
  1185.         const isHidden = finishedWork.memoizedState !== null;
    
  1186.         const newOffscreenSubtreeIsHidden =
    
  1187.           isHidden || offscreenSubtreeIsHidden;
    
  1188.         if (newOffscreenSubtreeIsHidden) {
    
  1189.           // The Offscreen tree is hidden. Skip over its layout effects.
    
  1190.         } else {
    
  1191.           // The Offscreen tree is visible.
    
  1192. 
    
  1193.           const wasHidden = current !== null && current.memoizedState !== null;
    
  1194.           const newOffscreenSubtreeWasHidden =
    
  1195.             wasHidden || offscreenSubtreeWasHidden;
    
  1196.           const prevOffscreenSubtreeIsHidden = offscreenSubtreeIsHidden;
    
  1197.           const prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
    
  1198.           offscreenSubtreeIsHidden = newOffscreenSubtreeIsHidden;
    
  1199.           offscreenSubtreeWasHidden = newOffscreenSubtreeWasHidden;
    
  1200. 
    
  1201.           if (offscreenSubtreeWasHidden && !prevOffscreenSubtreeWasHidden) {
    
  1202.             // This is the root of a reappearing boundary. As we continue
    
  1203.             // traversing the layout effects, we must also re-mount layout
    
  1204.             // effects that were unmounted when the Offscreen subtree was
    
  1205.             // hidden. So this is a superset of the normal commitLayoutEffects.
    
  1206.             const includeWorkInProgressEffects =
    
  1207.               (finishedWork.subtreeFlags & LayoutMask) !== NoFlags;
    
  1208.             recursivelyTraverseReappearLayoutEffects(
    
  1209.               finishedRoot,
    
  1210.               finishedWork,
    
  1211.               includeWorkInProgressEffects,
    
  1212.             );
    
  1213.           } else {
    
  1214.             recursivelyTraverseLayoutEffects(
    
  1215.               finishedRoot,
    
  1216.               finishedWork,
    
  1217.               committedLanes,
    
  1218.             );
    
  1219.           }
    
  1220.           offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden;
    
  1221.           offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
    
  1222.         }
    
  1223.       } else {
    
  1224.         recursivelyTraverseLayoutEffects(
    
  1225.           finishedRoot,
    
  1226.           finishedWork,
    
  1227.           committedLanes,
    
  1228.         );
    
  1229.       }
    
  1230.       if (flags & Ref) {
    
  1231.         const props: OffscreenProps = finishedWork.memoizedProps;
    
  1232.         if (props.mode === 'manual') {
    
  1233.           safelyAttachRef(finishedWork, finishedWork.return);
    
  1234.         } else {
    
  1235.           safelyDetachRef(finishedWork, finishedWork.return);
    
  1236.         }
    
  1237.       }
    
  1238.       break;
    
  1239.     }
    
  1240.     default: {
    
  1241.       recursivelyTraverseLayoutEffects(
    
  1242.         finishedRoot,
    
  1243.         finishedWork,
    
  1244.         committedLanes,
    
  1245.       );
    
  1246.       break;
    
  1247.     }
    
  1248.   }
    
  1249. }
    
  1250. 
    
  1251. function abortRootTransitions(
    
  1252.   root: FiberRoot,
    
  1253.   abort: TransitionAbort,
    
  1254.   deletedTransitions: Set<Transition>,
    
  1255.   deletedOffscreenInstance: OffscreenInstance | null,
    
  1256.   isInDeletedTree: boolean,
    
  1257. ) {
    
  1258.   if (enableTransitionTracing) {
    
  1259.     const rootTransitions = root.incompleteTransitions;
    
  1260.     deletedTransitions.forEach(transition => {
    
  1261.       if (rootTransitions.has(transition)) {
    
  1262.         const transitionInstance: TracingMarkerInstance = (rootTransitions.get(
    
  1263.           transition,
    
  1264.         ): any);
    
  1265.         if (transitionInstance.aborts === null) {
    
  1266.           transitionInstance.aborts = [];
    
  1267.         }
    
  1268.         transitionInstance.aborts.push(abort);
    
  1269. 
    
  1270.         if (deletedOffscreenInstance !== null) {
    
  1271.           if (
    
  1272.             transitionInstance.pendingBoundaries !== null &&
    
  1273.             transitionInstance.pendingBoundaries.has(deletedOffscreenInstance)
    
  1274.           ) {
    
  1275.             // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  1276.             transitionInstance.pendingBoundaries.delete(
    
  1277.               deletedOffscreenInstance,
    
  1278.             );
    
  1279.           }
    
  1280.         }
    
  1281.       }
    
  1282.     });
    
  1283.   }
    
  1284. }
    
  1285. 
    
  1286. function abortTracingMarkerTransitions(
    
  1287.   abortedFiber: Fiber,
    
  1288.   abort: TransitionAbort,
    
  1289.   deletedTransitions: Set<Transition>,
    
  1290.   deletedOffscreenInstance: OffscreenInstance | null,
    
  1291.   isInDeletedTree: boolean,
    
  1292. ) {
    
  1293.   if (enableTransitionTracing) {
    
  1294.     const markerInstance: TracingMarkerInstance = abortedFiber.stateNode;
    
  1295.     const markerTransitions = markerInstance.transitions;
    
  1296.     const pendingBoundaries = markerInstance.pendingBoundaries;
    
  1297.     if (markerTransitions !== null) {
    
  1298.       // TODO: Refactor this code. Is there a way to move this code to
    
  1299.       // the deletions phase instead of calculating it here while making sure
    
  1300.       // complete is called appropriately?
    
  1301.       deletedTransitions.forEach(transition => {
    
  1302.         // If one of the transitions on the tracing marker is a transition
    
  1303.         // that was in an aborted subtree, we will abort that tracing marker
    
  1304.         if (
    
  1305.           abortedFiber !== null &&
    
  1306.           markerTransitions.has(transition) &&
    
  1307.           (markerInstance.aborts === null ||
    
  1308.             !markerInstance.aborts.includes(abort))
    
  1309.         ) {
    
  1310.           if (markerInstance.transitions !== null) {
    
  1311.             if (markerInstance.aborts === null) {
    
  1312.               markerInstance.aborts = [abort];
    
  1313.               addMarkerIncompleteCallbackToPendingTransition(
    
  1314.                 abortedFiber.memoizedProps.name,
    
  1315.                 markerInstance.transitions,
    
  1316.                 markerInstance.aborts,
    
  1317.               );
    
  1318.             } else {
    
  1319.               markerInstance.aborts.push(abort);
    
  1320.             }
    
  1321. 
    
  1322.             // We only want to call onTransitionProgress when the marker hasn't been
    
  1323.             // deleted
    
  1324.             if (
    
  1325.               deletedOffscreenInstance !== null &&
    
  1326.               !isInDeletedTree &&
    
  1327.               pendingBoundaries !== null &&
    
  1328.               pendingBoundaries.has(deletedOffscreenInstance)
    
  1329.             ) {
    
  1330.               pendingBoundaries.delete(deletedOffscreenInstance);
    
  1331. 
    
  1332.               addMarkerProgressCallbackToPendingTransition(
    
  1333.                 abortedFiber.memoizedProps.name,
    
  1334.                 deletedTransitions,
    
  1335.                 pendingBoundaries,
    
  1336.               );
    
  1337.             }
    
  1338.           }
    
  1339.         }
    
  1340.       });
    
  1341.     }
    
  1342.   }
    
  1343. }
    
  1344. 
    
  1345. function abortParentMarkerTransitionsForDeletedFiber(
    
  1346.   abortedFiber: Fiber,
    
  1347.   abort: TransitionAbort,
    
  1348.   deletedTransitions: Set<Transition>,
    
  1349.   deletedOffscreenInstance: OffscreenInstance | null,
    
  1350.   isInDeletedTree: boolean,
    
  1351. ) {
    
  1352.   if (enableTransitionTracing) {
    
  1353.     // Find all pending markers that are waiting on child suspense boundaries in the
    
  1354.     // aborted subtree and cancels them
    
  1355.     let fiber: null | Fiber = abortedFiber;
    
  1356.     while (fiber !== null) {
    
  1357.       switch (fiber.tag) {
    
  1358.         case TracingMarkerComponent:
    
  1359.           abortTracingMarkerTransitions(
    
  1360.             fiber,
    
  1361.             abort,
    
  1362.             deletedTransitions,
    
  1363.             deletedOffscreenInstance,
    
  1364.             isInDeletedTree,
    
  1365.           );
    
  1366.           break;
    
  1367.         case HostRoot:
    
  1368.           const root = fiber.stateNode;
    
  1369.           abortRootTransitions(
    
  1370.             root,
    
  1371.             abort,
    
  1372.             deletedTransitions,
    
  1373.             deletedOffscreenInstance,
    
  1374.             isInDeletedTree,
    
  1375.           );
    
  1376. 
    
  1377.           break;
    
  1378.         default:
    
  1379.           break;
    
  1380.       }
    
  1381. 
    
  1382.       fiber = fiber.return;
    
  1383.     }
    
  1384.   }
    
  1385. }
    
  1386. 
    
  1387. function commitTransitionProgress(offscreenFiber: Fiber) {
    
  1388.   if (enableTransitionTracing) {
    
  1389.     // This function adds suspense boundaries to the root
    
  1390.     // or tracing marker's pendingBoundaries map.
    
  1391.     // When a suspense boundary goes from a resolved to a fallback
    
  1392.     // state we add the boundary to the map, and when it goes from
    
  1393.     // a fallback to a resolved state, we remove the boundary from
    
  1394.     // the map.
    
  1395. 
    
  1396.     // We use stateNode on the Offscreen component as a stable object
    
  1397.     // that doesnt change from render to render. This way we can
    
  1398.     // distinguish between different Offscreen instances (vs. the same
    
  1399.     // Offscreen instance with different fibers)
    
  1400.     const offscreenInstance: OffscreenInstance = offscreenFiber.stateNode;
    
  1401. 
    
  1402.     let prevState: SuspenseState | null = null;
    
  1403.     const previousFiber = offscreenFiber.alternate;
    
  1404.     if (previousFiber !== null && previousFiber.memoizedState !== null) {
    
  1405.       prevState = previousFiber.memoizedState;
    
  1406.     }
    
  1407.     const nextState: SuspenseState | null = offscreenFiber.memoizedState;
    
  1408. 
    
  1409.     const wasHidden = prevState !== null;
    
  1410.     const isHidden = nextState !== null;
    
  1411. 
    
  1412.     const pendingMarkers = offscreenInstance._pendingMarkers;
    
  1413.     // If there is a name on the suspense boundary, store that in
    
  1414.     // the pending boundaries.
    
  1415.     let name = null;
    
  1416.     const parent = offscreenFiber.return;
    
  1417.     if (
    
  1418.       parent !== null &&
    
  1419.       parent.tag === SuspenseComponent &&
    
  1420.       parent.memoizedProps.unstable_name
    
  1421.     ) {
    
  1422.       name = parent.memoizedProps.unstable_name;
    
  1423.     }
    
  1424. 
    
  1425.     if (!wasHidden && isHidden) {
    
  1426.       // The suspense boundaries was just hidden. Add the boundary
    
  1427.       // to the pending boundary set if it's there
    
  1428.       if (pendingMarkers !== null) {
    
  1429.         pendingMarkers.forEach(markerInstance => {
    
  1430.           const pendingBoundaries = markerInstance.pendingBoundaries;
    
  1431.           const transitions = markerInstance.transitions;
    
  1432.           const markerName = markerInstance.name;
    
  1433.           if (
    
  1434.             pendingBoundaries !== null &&
    
  1435.             !pendingBoundaries.has(offscreenInstance)
    
  1436.           ) {
    
  1437.             pendingBoundaries.set(offscreenInstance, {
    
  1438.               name,
    
  1439.             });
    
  1440.             if (transitions !== null) {
    
  1441.               if (
    
  1442.                 markerInstance.tag === TransitionTracingMarker &&
    
  1443.                 markerName !== null
    
  1444.               ) {
    
  1445.                 addMarkerProgressCallbackToPendingTransition(
    
  1446.                   markerName,
    
  1447.                   transitions,
    
  1448.                   pendingBoundaries,
    
  1449.                 );
    
  1450.               } else if (markerInstance.tag === TransitionRoot) {
    
  1451.                 transitions.forEach(transition => {
    
  1452.                   addTransitionProgressCallbackToPendingTransition(
    
  1453.                     transition,
    
  1454.                     pendingBoundaries,
    
  1455.                   );
    
  1456.                 });
    
  1457.               }
    
  1458.             }
    
  1459.           }
    
  1460.         });
    
  1461.       }
    
  1462.     } else if (wasHidden && !isHidden) {
    
  1463.       // The suspense boundary went from hidden to visible. Remove
    
  1464.       // the boundary from the pending suspense boundaries set
    
  1465.       // if it's there
    
  1466.       if (pendingMarkers !== null) {
    
  1467.         pendingMarkers.forEach(markerInstance => {
    
  1468.           const pendingBoundaries = markerInstance.pendingBoundaries;
    
  1469.           const transitions = markerInstance.transitions;
    
  1470.           const markerName = markerInstance.name;
    
  1471.           if (
    
  1472.             pendingBoundaries !== null &&
    
  1473.             pendingBoundaries.has(offscreenInstance)
    
  1474.           ) {
    
  1475.             pendingBoundaries.delete(offscreenInstance);
    
  1476.             if (transitions !== null) {
    
  1477.               if (
    
  1478.                 markerInstance.tag === TransitionTracingMarker &&
    
  1479.                 markerName !== null
    
  1480.               ) {
    
  1481.                 addMarkerProgressCallbackToPendingTransition(
    
  1482.                   markerName,
    
  1483.                   transitions,
    
  1484.                   pendingBoundaries,
    
  1485.                 );
    
  1486. 
    
  1487.                 // If there are no more unresolved suspense boundaries, the interaction
    
  1488.                 // is considered finished
    
  1489.                 if (pendingBoundaries.size === 0) {
    
  1490.                   if (markerInstance.aborts === null) {
    
  1491.                     addMarkerCompleteCallbackToPendingTransition(
    
  1492.                       markerName,
    
  1493.                       transitions,
    
  1494.                     );
    
  1495.                   }
    
  1496.                   markerInstance.transitions = null;
    
  1497.                   markerInstance.pendingBoundaries = null;
    
  1498.                   markerInstance.aborts = null;
    
  1499.                 }
    
  1500.               } else if (markerInstance.tag === TransitionRoot) {
    
  1501.                 transitions.forEach(transition => {
    
  1502.                   addTransitionProgressCallbackToPendingTransition(
    
  1503.                     transition,
    
  1504.                     pendingBoundaries,
    
  1505.                   );
    
  1506.                 });
    
  1507.               }
    
  1508.             }
    
  1509.           }
    
  1510.         });
    
  1511.       }
    
  1512.     }
    
  1513.   }
    
  1514. }
    
  1515. 
    
  1516. function hideOrUnhideAllChildren(finishedWork: Fiber, isHidden: boolean) {
    
  1517.   // Only hide or unhide the top-most host nodes.
    
  1518.   let hostSubtreeRoot = null;
    
  1519. 
    
  1520.   if (supportsMutation) {
    
  1521.     // We only have the top Fiber that was inserted but we need to recurse down its
    
  1522.     // children to find all the terminal nodes.
    
  1523.     let node: Fiber = finishedWork;
    
  1524.     while (true) {
    
  1525.       if (
    
  1526.         node.tag === HostComponent ||
    
  1527.         (enableFloat && supportsResources
    
  1528.           ? node.tag === HostHoistable
    
  1529.           : false) ||
    
  1530.         (enableHostSingletons && supportsSingletons
    
  1531.           ? node.tag === HostSingleton
    
  1532.           : false)
    
  1533.       ) {
    
  1534.         if (hostSubtreeRoot === null) {
    
  1535.           hostSubtreeRoot = node;
    
  1536.           try {
    
  1537.             const instance = node.stateNode;
    
  1538.             if (isHidden) {
    
  1539.               hideInstance(instance);
    
  1540.             } else {
    
  1541.               unhideInstance(node.stateNode, node.memoizedProps);
    
  1542.             }
    
  1543.           } catch (error) {
    
  1544.             captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  1545.           }
    
  1546.         }
    
  1547.       } else if (node.tag === HostText) {
    
  1548.         if (hostSubtreeRoot === null) {
    
  1549.           try {
    
  1550.             const instance = node.stateNode;
    
  1551.             if (isHidden) {
    
  1552.               hideTextInstance(instance);
    
  1553.             } else {
    
  1554.               unhideTextInstance(instance, node.memoizedProps);
    
  1555.             }
    
  1556.           } catch (error) {
    
  1557.             captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  1558.           }
    
  1559.         }
    
  1560.       } else if (
    
  1561.         (node.tag === OffscreenComponent ||
    
  1562.           node.tag === LegacyHiddenComponent) &&
    
  1563.         (node.memoizedState: OffscreenState) !== null &&
    
  1564.         node !== finishedWork
    
  1565.       ) {
    
  1566.         // Found a nested Offscreen component that is hidden.
    
  1567.         // Don't search any deeper. This tree should remain hidden.
    
  1568.       } else if (node.child !== null) {
    
  1569.         node.child.return = node;
    
  1570.         node = node.child;
    
  1571.         continue;
    
  1572.       }
    
  1573. 
    
  1574.       if (node === finishedWork) {
    
  1575.         return;
    
  1576.       }
    
  1577.       while (node.sibling === null) {
    
  1578.         if (node.return === null || node.return === finishedWork) {
    
  1579.           return;
    
  1580.         }
    
  1581. 
    
  1582.         if (hostSubtreeRoot === node) {
    
  1583.           hostSubtreeRoot = null;
    
  1584.         }
    
  1585. 
    
  1586.         node = node.return;
    
  1587.       }
    
  1588. 
    
  1589.       if (hostSubtreeRoot === node) {
    
  1590.         hostSubtreeRoot = null;
    
  1591.       }
    
  1592. 
    
  1593.       node.sibling.return = node.return;
    
  1594.       node = node.sibling;
    
  1595.     }
    
  1596.   }
    
  1597. }
    
  1598. 
    
  1599. function commitAttachRef(finishedWork: Fiber) {
    
  1600.   const ref = finishedWork.ref;
    
  1601.   if (ref !== null) {
    
  1602.     const instance = finishedWork.stateNode;
    
  1603.     let instanceToUse;
    
  1604.     switch (finishedWork.tag) {
    
  1605.       case HostHoistable:
    
  1606.       case HostSingleton:
    
  1607.       case HostComponent:
    
  1608.         instanceToUse = getPublicInstance(instance);
    
  1609.         break;
    
  1610.       default:
    
  1611.         instanceToUse = instance;
    
  1612.     }
    
  1613.     // Moved outside to ensure DCE works with this flag
    
  1614.     if (enableScopeAPI && finishedWork.tag === ScopeComponent) {
    
  1615.       instanceToUse = instance;
    
  1616.     }
    
  1617.     if (typeof ref === 'function') {
    
  1618.       if (shouldProfile(finishedWork)) {
    
  1619.         try {
    
  1620.           startLayoutEffectTimer();
    
  1621.           finishedWork.refCleanup = ref(instanceToUse);
    
  1622.         } finally {
    
  1623.           recordLayoutEffectDuration(finishedWork);
    
  1624.         }
    
  1625.       } else {
    
  1626.         finishedWork.refCleanup = ref(instanceToUse);
    
  1627.       }
    
  1628.     } else {
    
  1629.       if (__DEV__) {
    
  1630.         if (!ref.hasOwnProperty('current')) {
    
  1631.           console.error(
    
  1632.             'Unexpected ref object provided for %s. ' +
    
  1633.               'Use either a ref-setter function or React.createRef().',
    
  1634.             getComponentNameFromFiber(finishedWork),
    
  1635.           );
    
  1636.         }
    
  1637.       }
    
  1638. 
    
  1639.       // $FlowFixMe[incompatible-use] unable to narrow type to the non-function case
    
  1640.       ref.current = instanceToUse;
    
  1641.     }
    
  1642.   }
    
  1643. }
    
  1644. 
    
  1645. function detachFiberMutation(fiber: Fiber) {
    
  1646.   // Cut off the return pointer to disconnect it from the tree.
    
  1647.   // This enables us to detect and warn against state updates on an unmounted component.
    
  1648.   // It also prevents events from bubbling from within disconnected components.
    
  1649.   //
    
  1650.   // Ideally, we should also clear the child pointer of the parent alternate to let this
    
  1651.   // get GC:ed but we don't know which for sure which parent is the current
    
  1652.   // one so we'll settle for GC:ing the subtree of this child.
    
  1653.   // This child itself will be GC:ed when the parent updates the next time.
    
  1654.   //
    
  1655.   // Note that we can't clear child or sibling pointers yet.
    
  1656.   // They're needed for passive effects and for findDOMNode.
    
  1657.   // We defer those fields, and all other cleanup, to the passive phase (see detachFiberAfterEffects).
    
  1658.   //
    
  1659.   // Don't reset the alternate yet, either. We need that so we can detach the
    
  1660.   // alternate's fields in the passive phase. Clearing the return pointer is
    
  1661.   // sufficient for findDOMNode semantics.
    
  1662.   const alternate = fiber.alternate;
    
  1663.   if (alternate !== null) {
    
  1664.     alternate.return = null;
    
  1665.   }
    
  1666.   fiber.return = null;
    
  1667. }
    
  1668. 
    
  1669. function detachFiberAfterEffects(fiber: Fiber) {
    
  1670.   const alternate = fiber.alternate;
    
  1671.   if (alternate !== null) {
    
  1672.     fiber.alternate = null;
    
  1673.     detachFiberAfterEffects(alternate);
    
  1674.   }
    
  1675. 
    
  1676.   // Clear cyclical Fiber fields. This level alone is designed to roughly
    
  1677.   // approximate the planned Fiber refactor. In that world, `setState` will be
    
  1678.   // bound to a special "instance" object instead of a Fiber. The Instance
    
  1679.   // object will not have any of these fields. It will only be connected to
    
  1680.   // the fiber tree via a single link at the root. So if this level alone is
    
  1681.   // sufficient to fix memory issues, that bodes well for our plans.
    
  1682.   fiber.child = null;
    
  1683.   fiber.deletions = null;
    
  1684.   fiber.sibling = null;
    
  1685. 
    
  1686.   // The `stateNode` is cyclical because on host nodes it points to the host
    
  1687.   // tree, which has its own pointers to children, parents, and siblings.
    
  1688.   // The other host nodes also point back to fibers, so we should detach that
    
  1689.   // one, too.
    
  1690.   if (fiber.tag === HostComponent) {
    
  1691.     const hostInstance: Instance = fiber.stateNode;
    
  1692.     if (hostInstance !== null) {
    
  1693.       detachDeletedInstance(hostInstance);
    
  1694.     }
    
  1695.   }
    
  1696.   fiber.stateNode = null;
    
  1697. 
    
  1698.   if (__DEV__) {
    
  1699.     fiber._debugSource = null;
    
  1700.     fiber._debugOwner = null;
    
  1701.   }
    
  1702. 
    
  1703.   // Theoretically, nothing in here should be necessary, because we already
    
  1704.   // disconnected the fiber from the tree. So even if something leaks this
    
  1705.   // particular fiber, it won't leak anything else.
    
  1706.   fiber.return = null;
    
  1707.   fiber.dependencies = null;
    
  1708.   fiber.memoizedProps = null;
    
  1709.   fiber.memoizedState = null;
    
  1710.   fiber.pendingProps = null;
    
  1711.   fiber.stateNode = null;
    
  1712.   // TODO: Move to `commitPassiveUnmountInsideDeletedTreeOnFiber` instead.
    
  1713.   fiber.updateQueue = null;
    
  1714. }
    
  1715. 
    
  1716. function emptyPortalContainer(current: Fiber) {
    
  1717.   if (!supportsPersistence) {
    
  1718.     return;
    
  1719.   }
    
  1720. 
    
  1721.   const portal: {
    
  1722.     containerInfo: Container,
    
  1723.     pendingChildren: ChildSet,
    
  1724.     ...
    
  1725.   } = current.stateNode;
    
  1726.   const {containerInfo} = portal;
    
  1727.   const emptyChildSet = createContainerChildSet();
    
  1728.   replaceContainerChildren(containerInfo, emptyChildSet);
    
  1729. }
    
  1730. 
    
  1731. function getHostParentFiber(fiber: Fiber): Fiber {
    
  1732.   let parent = fiber.return;
    
  1733.   while (parent !== null) {
    
  1734.     if (isHostParent(parent)) {
    
  1735.       return parent;
    
  1736.     }
    
  1737.     parent = parent.return;
    
  1738.   }
    
  1739. 
    
  1740.   throw new Error(
    
  1741.     'Expected to find a host parent. This error is likely caused by a bug ' +
    
  1742.       'in React. Please file an issue.',
    
  1743.   );
    
  1744. }
    
  1745. 
    
  1746. function isHostParent(fiber: Fiber): boolean {
    
  1747.   return (
    
  1748.     fiber.tag === HostComponent ||
    
  1749.     fiber.tag === HostRoot ||
    
  1750.     (enableFloat && supportsResources ? fiber.tag === HostHoistable : false) ||
    
  1751.     (enableHostSingletons && supportsSingletons
    
  1752.       ? fiber.tag === HostSingleton
    
  1753.       : false) ||
    
  1754.     fiber.tag === HostPortal
    
  1755.   );
    
  1756. }
    
  1757. 
    
  1758. function getHostSibling(fiber: Fiber): ?Instance {
    
  1759.   // We're going to search forward into the tree until we find a sibling host
    
  1760.   // node. Unfortunately, if multiple insertions are done in a row we have to
    
  1761.   // search past them. This leads to exponential search for the next sibling.
    
  1762.   // TODO: Find a more efficient way to do this.
    
  1763.   let node: Fiber = fiber;
    
  1764.   siblings: while (true) {
    
  1765.     // If we didn't find anything, let's try the next sibling.
    
  1766.     while (node.sibling === null) {
    
  1767.       if (node.return === null || isHostParent(node.return)) {
    
  1768.         // If we pop out of the root or hit the parent the fiber we are the
    
  1769.         // last sibling.
    
  1770.         return null;
    
  1771.       }
    
  1772.       // $FlowFixMe[incompatible-type] found when upgrading Flow
    
  1773.       node = node.return;
    
  1774.     }
    
  1775.     node.sibling.return = node.return;
    
  1776.     node = node.sibling;
    
  1777.     while (
    
  1778.       node.tag !== HostComponent &&
    
  1779.       node.tag !== HostText &&
    
  1780.       (!(enableHostSingletons && supportsSingletons)
    
  1781.         ? true
    
  1782.         : node.tag !== HostSingleton) &&
    
  1783.       node.tag !== DehydratedFragment
    
  1784.     ) {
    
  1785.       // If it is not host node and, we might have a host node inside it.
    
  1786.       // Try to search down until we find one.
    
  1787.       if (node.flags & Placement) {
    
  1788.         // If we don't have a child, try the siblings instead.
    
  1789.         continue siblings;
    
  1790.       }
    
  1791.       // If we don't have a child, try the siblings instead.
    
  1792.       // We also skip portals because they are not part of this host tree.
    
  1793.       if (node.child === null || node.tag === HostPortal) {
    
  1794.         continue siblings;
    
  1795.       } else {
    
  1796.         node.child.return = node;
    
  1797.         node = node.child;
    
  1798.       }
    
  1799.     }
    
  1800.     // Check if this host node is stable or about to be placed.
    
  1801.     if (!(node.flags & Placement)) {
    
  1802.       // Found it!
    
  1803.       return node.stateNode;
    
  1804.     }
    
  1805.   }
    
  1806. }
    
  1807. 
    
  1808. function commitPlacement(finishedWork: Fiber): void {
    
  1809.   if (!supportsMutation) {
    
  1810.     return;
    
  1811.   }
    
  1812. 
    
  1813.   if (enableHostSingletons && supportsSingletons) {
    
  1814.     if (finishedWork.tag === HostSingleton) {
    
  1815.       // Singletons are already in the Host and don't need to be placed
    
  1816.       // Since they operate somewhat like Portals though their children will
    
  1817.       // have Placement and will get placed inside them
    
  1818.       return;
    
  1819.     }
    
  1820.   }
    
  1821.   // Recursively insert all host nodes into the parent.
    
  1822.   const parentFiber = getHostParentFiber(finishedWork);
    
  1823. 
    
  1824.   switch (parentFiber.tag) {
    
  1825.     case HostSingleton: {
    
  1826.       if (enableHostSingletons && supportsSingletons) {
    
  1827.         const parent: Instance = parentFiber.stateNode;
    
  1828.         const before = getHostSibling(finishedWork);
    
  1829.         // We only have the top Fiber that was inserted but we need to recurse down its
    
  1830.         // children to find all the terminal nodes.
    
  1831.         insertOrAppendPlacementNode(finishedWork, before, parent);
    
  1832.         break;
    
  1833.       }
    
  1834.       // Fall through
    
  1835.     }
    
  1836.     case HostComponent: {
    
  1837.       const parent: Instance = parentFiber.stateNode;
    
  1838.       if (parentFiber.flags & ContentReset) {
    
  1839.         // Reset the text content of the parent before doing any insertions
    
  1840.         resetTextContent(parent);
    
  1841.         // Clear ContentReset from the effect tag
    
  1842.         parentFiber.flags &= ~ContentReset;
    
  1843.       }
    
  1844. 
    
  1845.       const before = getHostSibling(finishedWork);
    
  1846.       // We only have the top Fiber that was inserted but we need to recurse down its
    
  1847.       // children to find all the terminal nodes.
    
  1848.       insertOrAppendPlacementNode(finishedWork, before, parent);
    
  1849.       break;
    
  1850.     }
    
  1851.     case HostRoot:
    
  1852.     case HostPortal: {
    
  1853.       const parent: Container = parentFiber.stateNode.containerInfo;
    
  1854.       const before = getHostSibling(finishedWork);
    
  1855.       insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
    
  1856.       break;
    
  1857.     }
    
  1858.     default:
    
  1859.       throw new Error(
    
  1860.         'Invalid host parent fiber. This error is likely caused by a bug ' +
    
  1861.           'in React. Please file an issue.',
    
  1862.       );
    
  1863.   }
    
  1864. }
    
  1865. 
    
  1866. function insertOrAppendPlacementNodeIntoContainer(
    
  1867.   node: Fiber,
    
  1868.   before: ?Instance,
    
  1869.   parent: Container,
    
  1870. ): void {
    
  1871.   const {tag} = node;
    
  1872.   const isHost = tag === HostComponent || tag === HostText;
    
  1873.   if (isHost) {
    
  1874.     const stateNode = node.stateNode;
    
  1875.     if (before) {
    
  1876.       insertInContainerBefore(parent, stateNode, before);
    
  1877.     } else {
    
  1878.       appendChildToContainer(parent, stateNode);
    
  1879.     }
    
  1880.   } else if (
    
  1881.     tag === HostPortal ||
    
  1882.     (enableHostSingletons && supportsSingletons ? tag === HostSingleton : false)
    
  1883.   ) {
    
  1884.     // If the insertion itself is a portal, then we don't want to traverse
    
  1885.     // down its children. Instead, we'll get insertions from each child in
    
  1886.     // the portal directly.
    
  1887.     // If the insertion is a HostSingleton then it will be placed independently
    
  1888.   } else {
    
  1889.     const child = node.child;
    
  1890.     if (child !== null) {
    
  1891.       insertOrAppendPlacementNodeIntoContainer(child, before, parent);
    
  1892.       let sibling = child.sibling;
    
  1893.       while (sibling !== null) {
    
  1894.         insertOrAppendPlacementNodeIntoContainer(sibling, before, parent);
    
  1895.         sibling = sibling.sibling;
    
  1896.       }
    
  1897.     }
    
  1898.   }
    
  1899. }
    
  1900. 
    
  1901. function insertOrAppendPlacementNode(
    
  1902.   node: Fiber,
    
  1903.   before: ?Instance,
    
  1904.   parent: Instance,
    
  1905. ): void {
    
  1906.   const {tag} = node;
    
  1907.   const isHost = tag === HostComponent || tag === HostText;
    
  1908.   if (isHost) {
    
  1909.     const stateNode = node.stateNode;
    
  1910.     if (before) {
    
  1911.       insertBefore(parent, stateNode, before);
    
  1912.     } else {
    
  1913.       appendChild(parent, stateNode);
    
  1914.     }
    
  1915.   } else if (
    
  1916.     tag === HostPortal ||
    
  1917.     (enableHostSingletons && supportsSingletons ? tag === HostSingleton : false)
    
  1918.   ) {
    
  1919.     // If the insertion itself is a portal, then we don't want to traverse
    
  1920.     // down its children. Instead, we'll get insertions from each child in
    
  1921.     // the portal directly.
    
  1922.     // If the insertion is a HostSingleton then it will be placed independently
    
  1923.   } else {
    
  1924.     const child = node.child;
    
  1925.     if (child !== null) {
    
  1926.       insertOrAppendPlacementNode(child, before, parent);
    
  1927.       let sibling = child.sibling;
    
  1928.       while (sibling !== null) {
    
  1929.         insertOrAppendPlacementNode(sibling, before, parent);
    
  1930.         sibling = sibling.sibling;
    
  1931.       }
    
  1932.     }
    
  1933.   }
    
  1934. }
    
  1935. 
    
  1936. // These are tracked on the stack as we recursively traverse a
    
  1937. // deleted subtree.
    
  1938. // TODO: Update these during the whole mutation phase, not just during
    
  1939. // a deletion.
    
  1940. let hostParent: Instance | Container | null = null;
    
  1941. let hostParentIsContainer: boolean = false;
    
  1942. 
    
  1943. function commitDeletionEffects(
    
  1944.   root: FiberRoot,
    
  1945.   returnFiber: Fiber,
    
  1946.   deletedFiber: Fiber,
    
  1947. ) {
    
  1948.   if (supportsMutation) {
    
  1949.     // We only have the top Fiber that was deleted but we need to recurse down its
    
  1950.     // children to find all the terminal nodes.
    
  1951. 
    
  1952.     // Recursively delete all host nodes from the parent, detach refs, clean
    
  1953.     // up mounted layout effects, and call componentWillUnmount.
    
  1954. 
    
  1955.     // We only need to remove the topmost host child in each branch. But then we
    
  1956.     // still need to keep traversing to unmount effects, refs, and cWU. TODO: We
    
  1957.     // could split this into two separate traversals functions, where the second
    
  1958.     // one doesn't include any removeChild logic. This is maybe the same
    
  1959.     // function as "disappearLayoutEffects" (or whatever that turns into after
    
  1960.     // the layout phase is refactored to use recursion).
    
  1961. 
    
  1962.     // Before starting, find the nearest host parent on the stack so we know
    
  1963.     // which instance/container to remove the children from.
    
  1964.     // TODO: Instead of searching up the fiber return path on every deletion, we
    
  1965.     // can track the nearest host component on the JS stack as we traverse the
    
  1966.     // tree during the commit phase. This would make insertions faster, too.
    
  1967.     let parent: null | Fiber = returnFiber;
    
  1968.     findParent: while (parent !== null) {
    
  1969.       switch (parent.tag) {
    
  1970.         case HostSingleton:
    
  1971.         case HostComponent: {
    
  1972.           hostParent = parent.stateNode;
    
  1973.           hostParentIsContainer = false;
    
  1974.           break findParent;
    
  1975.         }
    
  1976.         case HostRoot: {
    
  1977.           hostParent = parent.stateNode.containerInfo;
    
  1978.           hostParentIsContainer = true;
    
  1979.           break findParent;
    
  1980.         }
    
  1981.         case HostPortal: {
    
  1982.           hostParent = parent.stateNode.containerInfo;
    
  1983.           hostParentIsContainer = true;
    
  1984.           break findParent;
    
  1985.         }
    
  1986.       }
    
  1987.       parent = parent.return;
    
  1988.     }
    
  1989.     if (hostParent === null) {
    
  1990.       throw new Error(
    
  1991.         'Expected to find a host parent. This error is likely caused by ' +
    
  1992.           'a bug in React. Please file an issue.',
    
  1993.       );
    
  1994.     }
    
  1995. 
    
  1996.     commitDeletionEffectsOnFiber(root, returnFiber, deletedFiber);
    
  1997.     hostParent = null;
    
  1998.     hostParentIsContainer = false;
    
  1999.   } else {
    
  2000.     // Detach refs and call componentWillUnmount() on the whole subtree.
    
  2001.     commitDeletionEffectsOnFiber(root, returnFiber, deletedFiber);
    
  2002.   }
    
  2003. 
    
  2004.   detachFiberMutation(deletedFiber);
    
  2005. }
    
  2006. 
    
  2007. function recursivelyTraverseDeletionEffects(
    
  2008.   finishedRoot: FiberRoot,
    
  2009.   nearestMountedAncestor: Fiber,
    
  2010.   parent: Fiber,
    
  2011. ) {
    
  2012.   // TODO: Use a static flag to skip trees that don't have unmount effects
    
  2013.   let child = parent.child;
    
  2014.   while (child !== null) {
    
  2015.     commitDeletionEffectsOnFiber(finishedRoot, nearestMountedAncestor, child);
    
  2016.     child = child.sibling;
    
  2017.   }
    
  2018. }
    
  2019. 
    
  2020. function commitDeletionEffectsOnFiber(
    
  2021.   finishedRoot: FiberRoot,
    
  2022.   nearestMountedAncestor: Fiber,
    
  2023.   deletedFiber: Fiber,
    
  2024. ) {
    
  2025.   onCommitUnmount(deletedFiber);
    
  2026. 
    
  2027.   // The cases in this outer switch modify the stack before they traverse
    
  2028.   // into their subtree. There are simpler cases in the inner switch
    
  2029.   // that don't modify the stack.
    
  2030.   switch (deletedFiber.tag) {
    
  2031.     case HostHoistable: {
    
  2032.       if (enableFloat && supportsResources) {
    
  2033.         if (!offscreenSubtreeWasHidden) {
    
  2034.           safelyDetachRef(deletedFiber, nearestMountedAncestor);
    
  2035.         }
    
  2036.         recursivelyTraverseDeletionEffects(
    
  2037.           finishedRoot,
    
  2038.           nearestMountedAncestor,
    
  2039.           deletedFiber,
    
  2040.         );
    
  2041.         if (deletedFiber.memoizedState) {
    
  2042.           releaseResource(deletedFiber.memoizedState);
    
  2043.         } else if (deletedFiber.stateNode) {
    
  2044.           unmountHoistable(deletedFiber.stateNode);
    
  2045.         }
    
  2046.         return;
    
  2047.       }
    
  2048.       // Fall through
    
  2049.     }
    
  2050.     case HostSingleton: {
    
  2051.       if (enableHostSingletons && supportsSingletons) {
    
  2052.         if (!offscreenSubtreeWasHidden) {
    
  2053.           safelyDetachRef(deletedFiber, nearestMountedAncestor);
    
  2054.         }
    
  2055. 
    
  2056.         const prevHostParent = hostParent;
    
  2057.         const prevHostParentIsContainer = hostParentIsContainer;
    
  2058.         hostParent = deletedFiber.stateNode;
    
  2059.         recursivelyTraverseDeletionEffects(
    
  2060.           finishedRoot,
    
  2061.           nearestMountedAncestor,
    
  2062.           deletedFiber,
    
  2063.         );
    
  2064. 
    
  2065.         // Normally this is called in passive unmount effect phase however with
    
  2066.         // HostSingleton we warn if you acquire one that is already associated to
    
  2067.         // a different fiber. To increase our chances of avoiding this, specifically
    
  2068.         // if you keyed a HostSingleton so there will be a delete followed by a Placement
    
  2069.         // we treat detach eagerly here
    
  2070.         releaseSingletonInstance(deletedFiber.stateNode);
    
  2071. 
    
  2072.         hostParent = prevHostParent;
    
  2073.         hostParentIsContainer = prevHostParentIsContainer;
    
  2074. 
    
  2075.         return;
    
  2076.       }
    
  2077.       // Fall through
    
  2078.     }
    
  2079.     case HostComponent: {
    
  2080.       if (!offscreenSubtreeWasHidden) {
    
  2081.         safelyDetachRef(deletedFiber, nearestMountedAncestor);
    
  2082.       }
    
  2083.       // Intentional fallthrough to next branch
    
  2084.     }
    
  2085.     case HostText: {
    
  2086.       // We only need to remove the nearest host child. Set the host parent
    
  2087.       // to `null` on the stack to indicate that nested children don't
    
  2088.       // need to be removed.
    
  2089.       if (supportsMutation) {
    
  2090.         const prevHostParent = hostParent;
    
  2091.         const prevHostParentIsContainer = hostParentIsContainer;
    
  2092.         hostParent = null;
    
  2093.         recursivelyTraverseDeletionEffects(
    
  2094.           finishedRoot,
    
  2095.           nearestMountedAncestor,
    
  2096.           deletedFiber,
    
  2097.         );
    
  2098.         hostParent = prevHostParent;
    
  2099.         hostParentIsContainer = prevHostParentIsContainer;
    
  2100. 
    
  2101.         if (hostParent !== null) {
    
  2102.           // Now that all the child effects have unmounted, we can remove the
    
  2103.           // node from the tree.
    
  2104.           if (hostParentIsContainer) {
    
  2105.             removeChildFromContainer(
    
  2106.               ((hostParent: any): Container),
    
  2107.               (deletedFiber.stateNode: Instance | TextInstance),
    
  2108.             );
    
  2109.           } else {
    
  2110.             removeChild(
    
  2111.               ((hostParent: any): Instance),
    
  2112.               (deletedFiber.stateNode: Instance | TextInstance),
    
  2113.             );
    
  2114.           }
    
  2115.         }
    
  2116.       } else {
    
  2117.         recursivelyTraverseDeletionEffects(
    
  2118.           finishedRoot,
    
  2119.           nearestMountedAncestor,
    
  2120.           deletedFiber,
    
  2121.         );
    
  2122.       }
    
  2123.       return;
    
  2124.     }
    
  2125.     case DehydratedFragment: {
    
  2126.       if (enableSuspenseCallback) {
    
  2127.         const hydrationCallbacks = finishedRoot.hydrationCallbacks;
    
  2128.         if (hydrationCallbacks !== null) {
    
  2129.           const onDeleted = hydrationCallbacks.onDeleted;
    
  2130.           if (onDeleted) {
    
  2131.             onDeleted((deletedFiber.stateNode: SuspenseInstance));
    
  2132.           }
    
  2133.         }
    
  2134.       }
    
  2135. 
    
  2136.       // Dehydrated fragments don't have any children
    
  2137. 
    
  2138.       // Delete the dehydrated suspense boundary and all of its content.
    
  2139.       if (supportsMutation) {
    
  2140.         if (hostParent !== null) {
    
  2141.           if (hostParentIsContainer) {
    
  2142.             clearSuspenseBoundaryFromContainer(
    
  2143.               ((hostParent: any): Container),
    
  2144.               (deletedFiber.stateNode: SuspenseInstance),
    
  2145.             );
    
  2146.           } else {
    
  2147.             clearSuspenseBoundary(
    
  2148.               ((hostParent: any): Instance),
    
  2149.               (deletedFiber.stateNode: SuspenseInstance),
    
  2150.             );
    
  2151.           }
    
  2152.         }
    
  2153.       }
    
  2154.       return;
    
  2155.     }
    
  2156.     case HostPortal: {
    
  2157.       if (supportsMutation) {
    
  2158.         // When we go into a portal, it becomes the parent to remove from.
    
  2159.         const prevHostParent = hostParent;
    
  2160.         const prevHostParentIsContainer = hostParentIsContainer;
    
  2161.         hostParent = deletedFiber.stateNode.containerInfo;
    
  2162.         hostParentIsContainer = true;
    
  2163.         recursivelyTraverseDeletionEffects(
    
  2164.           finishedRoot,
    
  2165.           nearestMountedAncestor,
    
  2166.           deletedFiber,
    
  2167.         );
    
  2168.         hostParent = prevHostParent;
    
  2169.         hostParentIsContainer = prevHostParentIsContainer;
    
  2170.       } else {
    
  2171.         emptyPortalContainer(deletedFiber);
    
  2172. 
    
  2173.         recursivelyTraverseDeletionEffects(
    
  2174.           finishedRoot,
    
  2175.           nearestMountedAncestor,
    
  2176.           deletedFiber,
    
  2177.         );
    
  2178.       }
    
  2179.       return;
    
  2180.     }
    
  2181.     case FunctionComponent:
    
  2182.     case ForwardRef:
    
  2183.     case MemoComponent:
    
  2184.     case SimpleMemoComponent: {
    
  2185.       if (!offscreenSubtreeWasHidden) {
    
  2186.         const updateQueue: FunctionComponentUpdateQueue | null =
    
  2187.           (deletedFiber.updateQueue: any);
    
  2188.         if (updateQueue !== null) {
    
  2189.           const lastEffect = updateQueue.lastEffect;
    
  2190.           if (lastEffect !== null) {
    
  2191.             const firstEffect = lastEffect.next;
    
  2192. 
    
  2193.             let effect = firstEffect;
    
  2194.             do {
    
  2195.               const tag = effect.tag;
    
  2196.               const inst = effect.inst;
    
  2197.               const destroy = inst.destroy;
    
  2198.               if (destroy !== undefined) {
    
  2199.                 if ((tag & HookInsertion) !== NoHookEffect) {
    
  2200.                   inst.destroy = undefined;
    
  2201.                   safelyCallDestroy(
    
  2202.                     deletedFiber,
    
  2203.                     nearestMountedAncestor,
    
  2204.                     destroy,
    
  2205.                   );
    
  2206.                 } else if ((tag & HookLayout) !== NoHookEffect) {
    
  2207.                   if (enableSchedulingProfiler) {
    
  2208.                     markComponentLayoutEffectUnmountStarted(deletedFiber);
    
  2209.                   }
    
  2210. 
    
  2211.                   if (shouldProfile(deletedFiber)) {
    
  2212.                     startLayoutEffectTimer();
    
  2213.                     inst.destroy = undefined;
    
  2214.                     safelyCallDestroy(
    
  2215.                       deletedFiber,
    
  2216.                       nearestMountedAncestor,
    
  2217.                       destroy,
    
  2218.                     );
    
  2219.                     recordLayoutEffectDuration(deletedFiber);
    
  2220.                   } else {
    
  2221.                     inst.destroy = undefined;
    
  2222.                     safelyCallDestroy(
    
  2223.                       deletedFiber,
    
  2224.                       nearestMountedAncestor,
    
  2225.                       destroy,
    
  2226.                     );
    
  2227.                   }
    
  2228. 
    
  2229.                   if (enableSchedulingProfiler) {
    
  2230.                     markComponentLayoutEffectUnmountStopped();
    
  2231.                   }
    
  2232.                 }
    
  2233.               }
    
  2234.               effect = effect.next;
    
  2235.             } while (effect !== firstEffect);
    
  2236.           }
    
  2237.         }
    
  2238.       }
    
  2239. 
    
  2240.       recursivelyTraverseDeletionEffects(
    
  2241.         finishedRoot,
    
  2242.         nearestMountedAncestor,
    
  2243.         deletedFiber,
    
  2244.       );
    
  2245.       return;
    
  2246.     }
    
  2247.     case ClassComponent: {
    
  2248.       if (!offscreenSubtreeWasHidden) {
    
  2249.         safelyDetachRef(deletedFiber, nearestMountedAncestor);
    
  2250.         const instance = deletedFiber.stateNode;
    
  2251.         if (typeof instance.componentWillUnmount === 'function') {
    
  2252.           safelyCallComponentWillUnmount(
    
  2253.             deletedFiber,
    
  2254.             nearestMountedAncestor,
    
  2255.             instance,
    
  2256.           );
    
  2257.         }
    
  2258.       }
    
  2259.       recursivelyTraverseDeletionEffects(
    
  2260.         finishedRoot,
    
  2261.         nearestMountedAncestor,
    
  2262.         deletedFiber,
    
  2263.       );
    
  2264.       return;
    
  2265.     }
    
  2266.     case ScopeComponent: {
    
  2267.       if (enableScopeAPI) {
    
  2268.         safelyDetachRef(deletedFiber, nearestMountedAncestor);
    
  2269.       }
    
  2270.       recursivelyTraverseDeletionEffects(
    
  2271.         finishedRoot,
    
  2272.         nearestMountedAncestor,
    
  2273.         deletedFiber,
    
  2274.       );
    
  2275.       return;
    
  2276.     }
    
  2277.     case OffscreenComponent: {
    
  2278.       safelyDetachRef(deletedFiber, nearestMountedAncestor);
    
  2279.       if (deletedFiber.mode & ConcurrentMode) {
    
  2280.         // If this offscreen component is hidden, we already unmounted it. Before
    
  2281.         // deleting the children, track that it's already unmounted so that we
    
  2282.         // don't attempt to unmount the effects again.
    
  2283.         // TODO: If the tree is hidden, in most cases we should be able to skip
    
  2284.         // over the nested children entirely. An exception is we haven't yet found
    
  2285.         // the topmost host node to delete, which we already track on the stack.
    
  2286.         // But the other case is portals, which need to be detached no matter how
    
  2287.         // deeply they are nested. We should use a subtree flag to track whether a
    
  2288.         // subtree includes a nested portal.
    
  2289.         const prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
    
  2290.         offscreenSubtreeWasHidden =
    
  2291.           prevOffscreenSubtreeWasHidden || deletedFiber.memoizedState !== null;
    
  2292. 
    
  2293.         recursivelyTraverseDeletionEffects(
    
  2294.           finishedRoot,
    
  2295.           nearestMountedAncestor,
    
  2296.           deletedFiber,
    
  2297.         );
    
  2298.         offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
    
  2299.       } else {
    
  2300.         recursivelyTraverseDeletionEffects(
    
  2301.           finishedRoot,
    
  2302.           nearestMountedAncestor,
    
  2303.           deletedFiber,
    
  2304.         );
    
  2305.       }
    
  2306.       break;
    
  2307.     }
    
  2308.     default: {
    
  2309.       recursivelyTraverseDeletionEffects(
    
  2310.         finishedRoot,
    
  2311.         nearestMountedAncestor,
    
  2312.         deletedFiber,
    
  2313.       );
    
  2314.       return;
    
  2315.     }
    
  2316.   }
    
  2317. }
    
  2318. function commitSuspenseCallback(finishedWork: Fiber) {
    
  2319.   // TODO: Move this to passive phase
    
  2320.   const newState: SuspenseState | null = finishedWork.memoizedState;
    
  2321.   if (enableSuspenseCallback && newState !== null) {
    
  2322.     const suspenseCallback = finishedWork.memoizedProps.suspenseCallback;
    
  2323.     if (typeof suspenseCallback === 'function') {
    
  2324.       const retryQueue: RetryQueue | null = (finishedWork.updateQueue: any);
    
  2325.       if (retryQueue !== null) {
    
  2326.         suspenseCallback(new Set(retryQueue));
    
  2327.       }
    
  2328.     } else if (__DEV__) {
    
  2329.       if (suspenseCallback !== undefined) {
    
  2330.         console.error('Unexpected type for suspenseCallback.');
    
  2331.       }
    
  2332.     }
    
  2333.   }
    
  2334. }
    
  2335. 
    
  2336. function commitSuspenseHydrationCallbacks(
    
  2337.   finishedRoot: FiberRoot,
    
  2338.   finishedWork: Fiber,
    
  2339. ) {
    
  2340.   if (!supportsHydration) {
    
  2341.     return;
    
  2342.   }
    
  2343.   const newState: SuspenseState | null = finishedWork.memoizedState;
    
  2344.   if (newState === null) {
    
  2345.     const current = finishedWork.alternate;
    
  2346.     if (current !== null) {
    
  2347.       const prevState: SuspenseState | null = current.memoizedState;
    
  2348.       if (prevState !== null) {
    
  2349.         const suspenseInstance = prevState.dehydrated;
    
  2350.         if (suspenseInstance !== null) {
    
  2351.           try {
    
  2352.             commitHydratedSuspenseInstance(suspenseInstance);
    
  2353.             if (enableSuspenseCallback) {
    
  2354.               const hydrationCallbacks = finishedRoot.hydrationCallbacks;
    
  2355.               if (hydrationCallbacks !== null) {
    
  2356.                 const onHydrated = hydrationCallbacks.onHydrated;
    
  2357.                 if (onHydrated) {
    
  2358.                   onHydrated(suspenseInstance);
    
  2359.                 }
    
  2360.               }
    
  2361.             }
    
  2362.           } catch (error) {
    
  2363.             captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  2364.           }
    
  2365.         }
    
  2366.       }
    
  2367.     }
    
  2368.   }
    
  2369. }
    
  2370. 
    
  2371. function getRetryCache(finishedWork: Fiber) {
    
  2372.   // TODO: Unify the interface for the retry cache so we don't have to switch
    
  2373.   // on the tag like this.
    
  2374.   switch (finishedWork.tag) {
    
  2375.     case SuspenseComponent:
    
  2376.     case SuspenseListComponent: {
    
  2377.       let retryCache = finishedWork.stateNode;
    
  2378.       if (retryCache === null) {
    
  2379.         retryCache = finishedWork.stateNode = new PossiblyWeakSet();
    
  2380.       }
    
  2381.       return retryCache;
    
  2382.     }
    
  2383.     case OffscreenComponent: {
    
  2384.       const instance: OffscreenInstance = finishedWork.stateNode;
    
  2385.       let retryCache: null | Set<Wakeable> | WeakSet<Wakeable> =
    
  2386.         instance._retryCache;
    
  2387.       if (retryCache === null) {
    
  2388.         retryCache = instance._retryCache = new PossiblyWeakSet();
    
  2389.       }
    
  2390.       return retryCache;
    
  2391.     }
    
  2392.     default: {
    
  2393.       throw new Error(
    
  2394.         `Unexpected Suspense handler tag (${finishedWork.tag}). This is a ` +
    
  2395.           'bug in React.',
    
  2396.       );
    
  2397.     }
    
  2398.   }
    
  2399. }
    
  2400. 
    
  2401. export function detachOffscreenInstance(instance: OffscreenInstance): void {
    
  2402.   const fiber = instance._current;
    
  2403.   if (fiber === null) {
    
  2404.     throw new Error(
    
  2405.       'Calling Offscreen.detach before instance handle has been set.',
    
  2406.     );
    
  2407.   }
    
  2408. 
    
  2409.   if ((instance._pendingVisibility & OffscreenDetached) !== NoFlags) {
    
  2410.     // The instance is already detached, this is a noop.
    
  2411.     return;
    
  2412.   }
    
  2413. 
    
  2414.   // TODO: There is an opportunity to optimise this by not entering commit phase
    
  2415.   // and unmounting effects directly.
    
  2416.   const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
    
  2417.   if (root !== null) {
    
  2418.     instance._pendingVisibility |= OffscreenDetached;
    
  2419.     scheduleUpdateOnFiber(root, fiber, SyncLane);
    
  2420.   }
    
  2421. }
    
  2422. 
    
  2423. export function attachOffscreenInstance(instance: OffscreenInstance): void {
    
  2424.   const fiber = instance._current;
    
  2425.   if (fiber === null) {
    
  2426.     throw new Error(
    
  2427.       'Calling Offscreen.detach before instance handle has been set.',
    
  2428.     );
    
  2429.   }
    
  2430. 
    
  2431.   if ((instance._pendingVisibility & OffscreenDetached) === NoFlags) {
    
  2432.     // The instance is already attached, this is a noop.
    
  2433.     return;
    
  2434.   }
    
  2435. 
    
  2436.   const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
    
  2437.   if (root !== null) {
    
  2438.     instance._pendingVisibility &= ~OffscreenDetached;
    
  2439.     scheduleUpdateOnFiber(root, fiber, SyncLane);
    
  2440.   }
    
  2441. }
    
  2442. 
    
  2443. function attachSuspenseRetryListeners(
    
  2444.   finishedWork: Fiber,
    
  2445.   wakeables: RetryQueue,
    
  2446. ) {
    
  2447.   // If this boundary just timed out, then it will have a set of wakeables.
    
  2448.   // For each wakeable, attach a listener so that when it resolves, React
    
  2449.   // attempts to re-render the boundary in the primary (pre-timeout) state.
    
  2450.   const retryCache = getRetryCache(finishedWork);
    
  2451.   wakeables.forEach(wakeable => {
    
  2452.     // Memoize using the boundary fiber to prevent redundant listeners.
    
  2453.     const retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);
    
  2454.     if (!retryCache.has(wakeable)) {
    
  2455.       retryCache.add(wakeable);
    
  2456. 
    
  2457.       if (enableUpdaterTracking) {
    
  2458.         if (isDevToolsPresent) {
    
  2459.           if (inProgressLanes !== null && inProgressRoot !== null) {
    
  2460.             // If we have pending work still, associate the original updaters with it.
    
  2461.             restorePendingUpdaters(inProgressRoot, inProgressLanes);
    
  2462.           } else {
    
  2463.             throw Error(
    
  2464.               'Expected finished root and lanes to be set. This is a bug in React.',
    
  2465.             );
    
  2466.           }
    
  2467.         }
    
  2468.       }
    
  2469. 
    
  2470.       wakeable.then(retry, retry);
    
  2471.     }
    
  2472.   });
    
  2473. }
    
  2474. 
    
  2475. // This function detects when a Suspense boundary goes from visible to hidden.
    
  2476. // It returns false if the boundary is already hidden.
    
  2477. // TODO: Use an effect tag.
    
  2478. export function isSuspenseBoundaryBeingHidden(
    
  2479.   current: Fiber | null,
    
  2480.   finishedWork: Fiber,
    
  2481. ): boolean {
    
  2482.   if (current !== null) {
    
  2483.     const oldState: SuspenseState | null = current.memoizedState;
    
  2484.     if (oldState === null || oldState.dehydrated !== null) {
    
  2485.       const newState: SuspenseState | null = finishedWork.memoizedState;
    
  2486.       return newState !== null && newState.dehydrated === null;
    
  2487.     }
    
  2488.   }
    
  2489.   return false;
    
  2490. }
    
  2491. 
    
  2492. export function commitMutationEffects(
    
  2493.   root: FiberRoot,
    
  2494.   finishedWork: Fiber,
    
  2495.   committedLanes: Lanes,
    
  2496. ) {
    
  2497.   inProgressLanes = committedLanes;
    
  2498.   inProgressRoot = root;
    
  2499. 
    
  2500.   setCurrentDebugFiberInDEV(finishedWork);
    
  2501.   commitMutationEffectsOnFiber(finishedWork, root, committedLanes);
    
  2502.   setCurrentDebugFiberInDEV(finishedWork);
    
  2503. 
    
  2504.   inProgressLanes = null;
    
  2505.   inProgressRoot = null;
    
  2506. }
    
  2507. 
    
  2508. function recursivelyTraverseMutationEffects(
    
  2509.   root: FiberRoot,
    
  2510.   parentFiber: Fiber,
    
  2511.   lanes: Lanes,
    
  2512. ) {
    
  2513.   // Deletions effects can be scheduled on any fiber type. They need to happen
    
  2514.   // before the children effects hae fired.
    
  2515.   const deletions = parentFiber.deletions;
    
  2516.   if (deletions !== null) {
    
  2517.     for (let i = 0; i < deletions.length; i++) {
    
  2518.       const childToDelete = deletions[i];
    
  2519.       try {
    
  2520.         commitDeletionEffects(root, parentFiber, childToDelete);
    
  2521.       } catch (error) {
    
  2522.         captureCommitPhaseError(childToDelete, parentFiber, error);
    
  2523.       }
    
  2524.     }
    
  2525.   }
    
  2526. 
    
  2527.   const prevDebugFiber = getCurrentDebugFiberInDEV();
    
  2528.   if (parentFiber.subtreeFlags & MutationMask) {
    
  2529.     let child = parentFiber.child;
    
  2530.     while (child !== null) {
    
  2531.       setCurrentDebugFiberInDEV(child);
    
  2532.       commitMutationEffectsOnFiber(child, root, lanes);
    
  2533.       child = child.sibling;
    
  2534.     }
    
  2535.   }
    
  2536.   setCurrentDebugFiberInDEV(prevDebugFiber);
    
  2537. }
    
  2538. 
    
  2539. let currentHoistableRoot: HoistableRoot | null = null;
    
  2540. 
    
  2541. function commitMutationEffectsOnFiber(
    
  2542.   finishedWork: Fiber,
    
  2543.   root: FiberRoot,
    
  2544.   lanes: Lanes,
    
  2545. ) {
    
  2546.   const current = finishedWork.alternate;
    
  2547.   const flags = finishedWork.flags;
    
  2548. 
    
  2549.   // The effect flag should be checked *after* we refine the type of fiber,
    
  2550.   // because the fiber tag is more specific. An exception is any flag related
    
  2551.   // to reconciliation, because those can be set on all fiber types.
    
  2552.   switch (finishedWork.tag) {
    
  2553.     case FunctionComponent:
    
  2554.     case ForwardRef:
    
  2555.     case MemoComponent:
    
  2556.     case SimpleMemoComponent: {
    
  2557.       recursivelyTraverseMutationEffects(root, finishedWork, lanes);
    
  2558.       commitReconciliationEffects(finishedWork);
    
  2559. 
    
  2560.       if (flags & Update) {
    
  2561.         try {
    
  2562.           commitHookEffectListUnmount(
    
  2563.             HookInsertion | HookHasEffect,
    
  2564.             finishedWork,
    
  2565.             finishedWork.return,
    
  2566.           );
    
  2567.           commitHookEffectListMount(
    
  2568.             HookInsertion | HookHasEffect,
    
  2569.             finishedWork,
    
  2570.           );
    
  2571.         } catch (error) {
    
  2572.           captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  2573.         }
    
  2574.         // Layout effects are destroyed during the mutation phase so that all
    
  2575.         // destroy functions for all fibers are called before any create functions.
    
  2576.         // This prevents sibling component effects from interfering with each other,
    
  2577.         // e.g. a destroy function in one component should never override a ref set
    
  2578.         // by a create function in another component during the same commit.
    
  2579.         if (shouldProfile(finishedWork)) {
    
  2580.           try {
    
  2581.             startLayoutEffectTimer();
    
  2582.             commitHookEffectListUnmount(
    
  2583.               HookLayout | HookHasEffect,
    
  2584.               finishedWork,
    
  2585.               finishedWork.return,
    
  2586.             );
    
  2587.           } catch (error) {
    
  2588.             captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  2589.           }
    
  2590.           recordLayoutEffectDuration(finishedWork);
    
  2591.         } else {
    
  2592.           try {
    
  2593.             commitHookEffectListUnmount(
    
  2594.               HookLayout | HookHasEffect,
    
  2595.               finishedWork,
    
  2596.               finishedWork.return,
    
  2597.             );
    
  2598.           } catch (error) {
    
  2599.             captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  2600.           }
    
  2601.         }
    
  2602.       }
    
  2603.       return;
    
  2604.     }
    
  2605.     case ClassComponent: {
    
  2606.       recursivelyTraverseMutationEffects(root, finishedWork, lanes);
    
  2607.       commitReconciliationEffects(finishedWork);
    
  2608. 
    
  2609.       if (flags & Ref) {
    
  2610.         if (current !== null) {
    
  2611.           safelyDetachRef(current, current.return);
    
  2612.         }
    
  2613.       }
    
  2614. 
    
  2615.       if (flags & Callback && offscreenSubtreeIsHidden) {
    
  2616.         const updateQueue: UpdateQueue<mixed> | null =
    
  2617.           (finishedWork.updateQueue: any);
    
  2618.         if (updateQueue !== null) {
    
  2619.           deferHiddenCallbacks(updateQueue);
    
  2620.         }
    
  2621.       }
    
  2622.       return;
    
  2623.     }
    
  2624.     case HostHoistable: {
    
  2625.       if (enableFloat && supportsResources) {
    
  2626.         // We cast because we always set the root at the React root and so it cannot be
    
  2627.         // null while we are processing mutation effects
    
  2628.         const hoistableRoot: HoistableRoot = (currentHoistableRoot: any);
    
  2629.         recursivelyTraverseMutationEffects(root, finishedWork, lanes);
    
  2630.         commitReconciliationEffects(finishedWork);
    
  2631. 
    
  2632.         if (flags & Ref) {
    
  2633.           if (current !== null) {
    
  2634.             safelyDetachRef(current, current.return);
    
  2635.           }
    
  2636.         }
    
  2637. 
    
  2638.         if (flags & Update) {
    
  2639.           const currentResource =
    
  2640.             current !== null ? current.memoizedState : null;
    
  2641.           const newResource = finishedWork.memoizedState;
    
  2642.           if (current === null) {
    
  2643.             // We are mounting a new HostHoistable Fiber. We fork the mount
    
  2644.             // behavior based on whether this instance is a Hoistable Instance
    
  2645.             // or a Hoistable Resource
    
  2646.             if (newResource === null) {
    
  2647.               if (finishedWork.stateNode === null) {
    
  2648.                 finishedWork.stateNode = hydrateHoistable(
    
  2649.                   hoistableRoot,
    
  2650.                   finishedWork.type,
    
  2651.                   finishedWork.memoizedProps,
    
  2652.                   finishedWork,
    
  2653.                 );
    
  2654.               } else {
    
  2655.                 mountHoistable(
    
  2656.                   hoistableRoot,
    
  2657.                   finishedWork.type,
    
  2658.                   finishedWork.stateNode,
    
  2659.                 );
    
  2660.               }
    
  2661.             } else {
    
  2662.               finishedWork.stateNode = acquireResource(
    
  2663.                 hoistableRoot,
    
  2664.                 newResource,
    
  2665.                 finishedWork.memoizedProps,
    
  2666.               );
    
  2667.             }
    
  2668.           } else if (currentResource !== newResource) {
    
  2669.             // We are moving to or from Hoistable Resource, or between different Hoistable Resources
    
  2670.             if (currentResource === null) {
    
  2671.               if (current.stateNode !== null) {
    
  2672.                 unmountHoistable(current.stateNode);
    
  2673.               }
    
  2674.             } else {
    
  2675.               releaseResource(currentResource);
    
  2676.             }
    
  2677.             if (newResource === null) {
    
  2678.               mountHoistable(
    
  2679.                 hoistableRoot,
    
  2680.                 finishedWork.type,
    
  2681.                 finishedWork.stateNode,
    
  2682.               );
    
  2683.             } else {
    
  2684.               acquireResource(
    
  2685.                 hoistableRoot,
    
  2686.                 newResource,
    
  2687.                 finishedWork.memoizedProps,
    
  2688.               );
    
  2689.             }
    
  2690.           } else if (newResource === null && finishedWork.stateNode !== null) {
    
  2691.             // We may have an update on a Hoistable element
    
  2692.             const updatePayload: null | UpdatePayload =
    
  2693.               (finishedWork.updateQueue: any);
    
  2694.             finishedWork.updateQueue = null;
    
  2695.             try {
    
  2696.               commitUpdate(
    
  2697.                 finishedWork.stateNode,
    
  2698.                 updatePayload,
    
  2699.                 finishedWork.type,
    
  2700.                 current.memoizedProps,
    
  2701.                 finishedWork.memoizedProps,
    
  2702.                 finishedWork,
    
  2703.               );
    
  2704.             } catch (error) {
    
  2705.               captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  2706.             }
    
  2707.           }
    
  2708.         }
    
  2709.         return;
    
  2710.       }
    
  2711.       // Fall through
    
  2712.     }
    
  2713.     case HostSingleton: {
    
  2714.       if (enableHostSingletons && supportsSingletons) {
    
  2715.         if (flags & Update) {
    
  2716.           const previousWork = finishedWork.alternate;
    
  2717.           if (previousWork === null) {
    
  2718.             const singleton = finishedWork.stateNode;
    
  2719.             const props = finishedWork.memoizedProps;
    
  2720.             // This was a new mount, we need to clear and set initial properties
    
  2721.             clearSingleton(singleton);
    
  2722.             acquireSingletonInstance(
    
  2723.               finishedWork.type,
    
  2724.               props,
    
  2725.               singleton,
    
  2726.               finishedWork,
    
  2727.             );
    
  2728.           }
    
  2729.         }
    
  2730.       }
    
  2731.       // Fall through
    
  2732.     }
    
  2733.     case HostComponent: {
    
  2734.       recursivelyTraverseMutationEffects(root, finishedWork, lanes);
    
  2735.       commitReconciliationEffects(finishedWork);
    
  2736. 
    
  2737.       if (flags & Ref) {
    
  2738.         if (current !== null) {
    
  2739.           safelyDetachRef(current, current.return);
    
  2740.         }
    
  2741.       }
    
  2742.       if (supportsMutation) {
    
  2743.         // TODO: ContentReset gets cleared by the children during the commit
    
  2744.         // phase. This is a refactor hazard because it means we must read
    
  2745.         // flags the flags after `commitReconciliationEffects` has already run;
    
  2746.         // the order matters. We should refactor so that ContentReset does not
    
  2747.         // rely on mutating the flag during commit. Like by setting a flag
    
  2748.         // during the render phase instead.
    
  2749.         if (finishedWork.flags & ContentReset) {
    
  2750.           const instance: Instance = finishedWork.stateNode;
    
  2751.           try {
    
  2752.             resetTextContent(instance);
    
  2753.           } catch (error) {
    
  2754.             captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  2755.           }
    
  2756.         }
    
  2757. 
    
  2758.         if (flags & Update) {
    
  2759.           const instance: Instance = finishedWork.stateNode;
    
  2760.           if (instance != null) {
    
  2761.             // Commit the work prepared earlier.
    
  2762.             const newProps = finishedWork.memoizedProps;
    
  2763.             // For hydration we reuse the update path but we treat the oldProps
    
  2764.             // as the newProps. The updatePayload will contain the real change in
    
  2765.             // this case.
    
  2766.             const oldProps =
    
  2767.               current !== null ? current.memoizedProps : newProps;
    
  2768.             const type = finishedWork.type;
    
  2769.             // TODO: Type the updateQueue to be specific to host components.
    
  2770.             const updatePayload: null | UpdatePayload =
    
  2771.               (finishedWork.updateQueue: any);
    
  2772.             finishedWork.updateQueue = null;
    
  2773.             try {
    
  2774.               commitUpdate(
    
  2775.                 instance,
    
  2776.                 updatePayload,
    
  2777.                 type,
    
  2778.                 oldProps,
    
  2779.                 newProps,
    
  2780.                 finishedWork,
    
  2781.               );
    
  2782.             } catch (error) {
    
  2783.               captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  2784.             }
    
  2785.           }
    
  2786.         }
    
  2787.       }
    
  2788.       return;
    
  2789.     }
    
  2790.     case HostText: {
    
  2791.       recursivelyTraverseMutationEffects(root, finishedWork, lanes);
    
  2792.       commitReconciliationEffects(finishedWork);
    
  2793. 
    
  2794.       if (flags & Update) {
    
  2795.         if (supportsMutation) {
    
  2796.           if (finishedWork.stateNode === null) {
    
  2797.             throw new Error(
    
  2798.               'This should have a text node initialized. This error is likely ' +
    
  2799.                 'caused by a bug in React. Please file an issue.',
    
  2800.             );
    
  2801.           }
    
  2802. 
    
  2803.           const textInstance: TextInstance = finishedWork.stateNode;
    
  2804.           const newText: string = finishedWork.memoizedProps;
    
  2805.           // For hydration we reuse the update path but we treat the oldProps
    
  2806.           // as the newProps. The updatePayload will contain the real change in
    
  2807.           // this case.
    
  2808.           const oldText: string =
    
  2809.             current !== null ? current.memoizedProps : newText;
    
  2810. 
    
  2811.           try {
    
  2812.             commitTextUpdate(textInstance, oldText, newText);
    
  2813.           } catch (error) {
    
  2814.             captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  2815.           }
    
  2816.         }
    
  2817.       }
    
  2818.       return;
    
  2819.     }
    
  2820.     case HostRoot: {
    
  2821.       if (enableFloat && supportsResources) {
    
  2822.         prepareToCommitHoistables();
    
  2823. 
    
  2824.         const previousHoistableRoot = currentHoistableRoot;
    
  2825.         currentHoistableRoot = getHoistableRoot(root.containerInfo);
    
  2826. 
    
  2827.         recursivelyTraverseMutationEffects(root, finishedWork, lanes);
    
  2828.         currentHoistableRoot = previousHoistableRoot;
    
  2829. 
    
  2830.         commitReconciliationEffects(finishedWork);
    
  2831.       } else {
    
  2832.         recursivelyTraverseMutationEffects(root, finishedWork, lanes);
    
  2833.         commitReconciliationEffects(finishedWork);
    
  2834.       }
    
  2835. 
    
  2836.       if (flags & Update) {
    
  2837.         if (supportsMutation && supportsHydration) {
    
  2838.           if (current !== null) {
    
  2839.             const prevRootState: RootState = current.memoizedState;
    
  2840.             if (prevRootState.isDehydrated) {
    
  2841.               try {
    
  2842.                 commitHydratedContainer(root.containerInfo);
    
  2843.               } catch (error) {
    
  2844.                 captureCommitPhaseError(
    
  2845.                   finishedWork,
    
  2846.                   finishedWork.return,
    
  2847.                   error,
    
  2848.                 );
    
  2849.               }
    
  2850.             }
    
  2851.           }
    
  2852.         }
    
  2853.         if (supportsPersistence) {
    
  2854.           const containerInfo = root.containerInfo;
    
  2855.           const pendingChildren = root.pendingChildren;
    
  2856.           try {
    
  2857.             replaceContainerChildren(containerInfo, pendingChildren);
    
  2858.           } catch (error) {
    
  2859.             captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  2860.           }
    
  2861.         }
    
  2862.       }
    
  2863.       return;
    
  2864.     }
    
  2865.     case HostPortal: {
    
  2866.       if (enableFloat && supportsResources) {
    
  2867.         const previousHoistableRoot = currentHoistableRoot;
    
  2868.         currentHoistableRoot = getHoistableRoot(
    
  2869.           finishedWork.stateNode.containerInfo,
    
  2870.         );
    
  2871.         recursivelyTraverseMutationEffects(root, finishedWork, lanes);
    
  2872.         commitReconciliationEffects(finishedWork);
    
  2873.         currentHoistableRoot = previousHoistableRoot;
    
  2874.       } else {
    
  2875.         recursivelyTraverseMutationEffects(root, finishedWork, lanes);
    
  2876.         commitReconciliationEffects(finishedWork);
    
  2877.       }
    
  2878. 
    
  2879.       if (flags & Update) {
    
  2880.         if (supportsPersistence) {
    
  2881.           const portal = finishedWork.stateNode;
    
  2882.           const containerInfo = portal.containerInfo;
    
  2883.           const pendingChildren = portal.pendingChildren;
    
  2884.           try {
    
  2885.             replaceContainerChildren(containerInfo, pendingChildren);
    
  2886.           } catch (error) {
    
  2887.             captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  2888.           }
    
  2889.         }
    
  2890.       }
    
  2891.       return;
    
  2892.     }
    
  2893.     case SuspenseComponent: {
    
  2894.       recursivelyTraverseMutationEffects(root, finishedWork, lanes);
    
  2895.       commitReconciliationEffects(finishedWork);
    
  2896. 
    
  2897.       // TODO: We should mark a flag on the Suspense fiber itself, rather than
    
  2898.       // relying on the Offscreen fiber having a flag also being marked. The
    
  2899.       // reason is that this offscreen fiber might not be part of the work-in-
    
  2900.       // progress tree! It could have been reused from a previous render. This
    
  2901.       // doesn't lead to incorrect behavior because we don't rely on the flag
    
  2902.       // check alone; we also compare the states explicitly below. But for
    
  2903.       // modeling purposes, we _should_ be able to rely on the flag check alone.
    
  2904.       // So this is a bit fragile.
    
  2905.       //
    
  2906.       // Also, all this logic could/should move to the passive phase so it
    
  2907.       // doesn't block paint.
    
  2908.       const offscreenFiber: Fiber = (finishedWork.child: any);
    
  2909.       if (offscreenFiber.flags & Visibility) {
    
  2910.         // Throttle the appearance and disappearance of Suspense fallbacks.
    
  2911.         const isShowingFallback =
    
  2912.           (finishedWork.memoizedState: SuspenseState | null) !== null;
    
  2913.         const wasShowingFallback =
    
  2914.           current !== null &&
    
  2915.           (current.memoizedState: SuspenseState | null) !== null;
    
  2916. 
    
  2917.         if (alwaysThrottleRetries) {
    
  2918.           if (isShowingFallback !== wasShowingFallback) {
    
  2919.             // A fallback is either appearing or disappearing.
    
  2920.             markCommitTimeOfFallback();
    
  2921.           }
    
  2922.         } else {
    
  2923.           if (isShowingFallback && !wasShowingFallback) {
    
  2924.             // Old behavior. Only mark when a fallback appears, not when
    
  2925.             // it disappears.
    
  2926.             markCommitTimeOfFallback();
    
  2927.           }
    
  2928.         }
    
  2929.       }
    
  2930. 
    
  2931.       if (flags & Update) {
    
  2932.         try {
    
  2933.           commitSuspenseCallback(finishedWork);
    
  2934.         } catch (error) {
    
  2935.           captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  2936.         }
    
  2937.         const retryQueue: RetryQueue | null = (finishedWork.updateQueue: any);
    
  2938.         if (retryQueue !== null) {
    
  2939.           finishedWork.updateQueue = null;
    
  2940.           attachSuspenseRetryListeners(finishedWork, retryQueue);
    
  2941.         }
    
  2942.       }
    
  2943.       return;
    
  2944.     }
    
  2945.     case OffscreenComponent: {
    
  2946.       if (flags & Ref) {
    
  2947.         if (current !== null) {
    
  2948.           safelyDetachRef(current, current.return);
    
  2949.         }
    
  2950.       }
    
  2951. 
    
  2952.       const newState: OffscreenState | null = finishedWork.memoizedState;
    
  2953.       const isHidden = newState !== null;
    
  2954.       const wasHidden = current !== null && current.memoizedState !== null;
    
  2955. 
    
  2956.       if (finishedWork.mode & ConcurrentMode) {
    
  2957.         // Before committing the children, track on the stack whether this
    
  2958.         // offscreen subtree was already hidden, so that we don't unmount the
    
  2959.         // effects again.
    
  2960.         const prevOffscreenSubtreeIsHidden = offscreenSubtreeIsHidden;
    
  2961.         const prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
    
  2962.         offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden || isHidden;
    
  2963.         offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden || wasHidden;
    
  2964.         recursivelyTraverseMutationEffects(root, finishedWork, lanes);
    
  2965.         offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
    
  2966.         offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden;
    
  2967.       } else {
    
  2968.         recursivelyTraverseMutationEffects(root, finishedWork, lanes);
    
  2969.       }
    
  2970. 
    
  2971.       commitReconciliationEffects(finishedWork);
    
  2972. 
    
  2973.       const offscreenInstance: OffscreenInstance = finishedWork.stateNode;
    
  2974. 
    
  2975.       // TODO: Add explicit effect flag to set _current.
    
  2976.       offscreenInstance._current = finishedWork;
    
  2977. 
    
  2978.       // Offscreen stores pending changes to visibility in `_pendingVisibility`. This is
    
  2979.       // to support batching of `attach` and `detach` calls.
    
  2980.       offscreenInstance._visibility &= ~OffscreenDetached;
    
  2981.       offscreenInstance._visibility |=
    
  2982.         offscreenInstance._pendingVisibility & OffscreenDetached;
    
  2983. 
    
  2984.       if (flags & Visibility) {
    
  2985.         // Track the current state on the Offscreen instance so we can
    
  2986.         // read it during an event
    
  2987.         if (isHidden) {
    
  2988.           offscreenInstance._visibility &= ~OffscreenVisible;
    
  2989.         } else {
    
  2990.           offscreenInstance._visibility |= OffscreenVisible;
    
  2991.         }
    
  2992. 
    
  2993.         if (isHidden) {
    
  2994.           const isUpdate = current !== null;
    
  2995.           const wasHiddenByAncestorOffscreen =
    
  2996.             offscreenSubtreeIsHidden || offscreenSubtreeWasHidden;
    
  2997.           // Only trigger disapper layout effects if:
    
  2998.           //   - This is an update, not first mount.
    
  2999.           //   - This Offscreen was not hidden before.
    
  3000.           //   - Ancestor Offscreen was not hidden in previous commit.
    
  3001.           if (isUpdate && !wasHidden && !wasHiddenByAncestorOffscreen) {
    
  3002.             if ((finishedWork.mode & ConcurrentMode) !== NoMode) {
    
  3003.               // Disappear the layout effects of all the children
    
  3004.               recursivelyTraverseDisappearLayoutEffects(finishedWork);
    
  3005.             }
    
  3006.           }
    
  3007.         } else {
    
  3008.           if (wasHidden) {
    
  3009.             // TODO: Move re-appear call here for symmetry?
    
  3010.           }
    
  3011.         }
    
  3012. 
    
  3013.         // Offscreen with manual mode manages visibility manually.
    
  3014.         if (supportsMutation && !isOffscreenManual(finishedWork)) {
    
  3015.           // TODO: This needs to run whenever there's an insertion or update
    
  3016.           // inside a hidden Offscreen tree.
    
  3017.           hideOrUnhideAllChildren(finishedWork, isHidden);
    
  3018.         }
    
  3019.       }
    
  3020. 
    
  3021.       // TODO: Move to passive phase
    
  3022.       if (flags & Update) {
    
  3023.         const offscreenQueue: OffscreenQueue | null =
    
  3024.           (finishedWork.updateQueue: any);
    
  3025.         if (offscreenQueue !== null) {
    
  3026.           const retryQueue = offscreenQueue.retryQueue;
    
  3027.           if (retryQueue !== null) {
    
  3028.             offscreenQueue.retryQueue = null;
    
  3029.             attachSuspenseRetryListeners(finishedWork, retryQueue);
    
  3030.           }
    
  3031.         }
    
  3032.       }
    
  3033.       return;
    
  3034.     }
    
  3035.     case SuspenseListComponent: {
    
  3036.       recursivelyTraverseMutationEffects(root, finishedWork, lanes);
    
  3037.       commitReconciliationEffects(finishedWork);
    
  3038. 
    
  3039.       if (flags & Update) {
    
  3040.         const retryQueue: Set<Wakeable> | null =
    
  3041.           (finishedWork.updateQueue: any);
    
  3042.         if (retryQueue !== null) {
    
  3043.           finishedWork.updateQueue = null;
    
  3044.           attachSuspenseRetryListeners(finishedWork, retryQueue);
    
  3045.         }
    
  3046.       }
    
  3047.       return;
    
  3048.     }
    
  3049.     case ScopeComponent: {
    
  3050.       if (enableScopeAPI) {
    
  3051.         recursivelyTraverseMutationEffects(root, finishedWork, lanes);
    
  3052.         commitReconciliationEffects(finishedWork);
    
  3053. 
    
  3054.         // TODO: This is a temporary solution that allowed us to transition away
    
  3055.         // from React Flare on www.
    
  3056.         if (flags & Ref) {
    
  3057.           if (current !== null) {
    
  3058.             safelyDetachRef(finishedWork, finishedWork.return);
    
  3059.           }
    
  3060.           safelyAttachRef(finishedWork, finishedWork.return);
    
  3061.         }
    
  3062.         if (flags & Update) {
    
  3063.           const scopeInstance = finishedWork.stateNode;
    
  3064.           prepareScopeUpdate(scopeInstance, finishedWork);
    
  3065.         }
    
  3066.       }
    
  3067.       return;
    
  3068.     }
    
  3069.     default: {
    
  3070.       recursivelyTraverseMutationEffects(root, finishedWork, lanes);
    
  3071.       commitReconciliationEffects(finishedWork);
    
  3072. 
    
  3073.       return;
    
  3074.     }
    
  3075.   }
    
  3076. }
    
  3077. function commitReconciliationEffects(finishedWork: Fiber) {
    
  3078.   // Placement effects (insertions, reorders) can be scheduled on any fiber
    
  3079.   // type. They needs to happen after the children effects have fired, but
    
  3080.   // before the effects on this fiber have fired.
    
  3081.   const flags = finishedWork.flags;
    
  3082.   if (flags & Placement) {
    
  3083.     try {
    
  3084.       commitPlacement(finishedWork);
    
  3085.     } catch (error) {
    
  3086.       captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  3087.     }
    
  3088.     // Clear the "placement" from effect tag so that we know that this is
    
  3089.     // inserted, before any life-cycles like componentDidMount gets called.
    
  3090.     // TODO: findDOMNode doesn't rely on this any more but isMounted does
    
  3091.     // and isMounted is deprecated anyway so we should be able to kill this.
    
  3092.     finishedWork.flags &= ~Placement;
    
  3093.   }
    
  3094.   if (flags & Hydrating) {
    
  3095.     finishedWork.flags &= ~Hydrating;
    
  3096.   }
    
  3097. }
    
  3098. 
    
  3099. export function commitLayoutEffects(
    
  3100.   finishedWork: Fiber,
    
  3101.   root: FiberRoot,
    
  3102.   committedLanes: Lanes,
    
  3103. ): void {
    
  3104.   inProgressLanes = committedLanes;
    
  3105.   inProgressRoot = root;
    
  3106. 
    
  3107.   const current = finishedWork.alternate;
    
  3108.   commitLayoutEffectOnFiber(root, current, finishedWork, committedLanes);
    
  3109. 
    
  3110.   inProgressLanes = null;
    
  3111.   inProgressRoot = null;
    
  3112. }
    
  3113. 
    
  3114. function recursivelyTraverseLayoutEffects(
    
  3115.   root: FiberRoot,
    
  3116.   parentFiber: Fiber,
    
  3117.   lanes: Lanes,
    
  3118. ) {
    
  3119.   const prevDebugFiber = getCurrentDebugFiberInDEV();
    
  3120.   if (parentFiber.subtreeFlags & LayoutMask) {
    
  3121.     let child = parentFiber.child;
    
  3122.     while (child !== null) {
    
  3123.       setCurrentDebugFiberInDEV(child);
    
  3124.       const current = child.alternate;
    
  3125.       commitLayoutEffectOnFiber(root, current, child, lanes);
    
  3126.       child = child.sibling;
    
  3127.     }
    
  3128.   }
    
  3129.   setCurrentDebugFiberInDEV(prevDebugFiber);
    
  3130. }
    
  3131. 
    
  3132. export function disappearLayoutEffects(finishedWork: Fiber) {
    
  3133.   switch (finishedWork.tag) {
    
  3134.     case FunctionComponent:
    
  3135.     case ForwardRef:
    
  3136.     case MemoComponent:
    
  3137.     case SimpleMemoComponent: {
    
  3138.       // TODO (Offscreen) Check: flags & LayoutStatic
    
  3139.       if (shouldProfile(finishedWork)) {
    
  3140.         try {
    
  3141.           startLayoutEffectTimer();
    
  3142.           commitHookEffectListUnmount(
    
  3143.             HookLayout,
    
  3144.             finishedWork,
    
  3145.             finishedWork.return,
    
  3146.           );
    
  3147.         } finally {
    
  3148.           recordLayoutEffectDuration(finishedWork);
    
  3149.         }
    
  3150.       } else {
    
  3151.         commitHookEffectListUnmount(
    
  3152.           HookLayout,
    
  3153.           finishedWork,
    
  3154.           finishedWork.return,
    
  3155.         );
    
  3156.       }
    
  3157. 
    
  3158.       recursivelyTraverseDisappearLayoutEffects(finishedWork);
    
  3159.       break;
    
  3160.     }
    
  3161.     case ClassComponent: {
    
  3162.       // TODO (Offscreen) Check: flags & RefStatic
    
  3163.       safelyDetachRef(finishedWork, finishedWork.return);
    
  3164. 
    
  3165.       const instance = finishedWork.stateNode;
    
  3166.       if (typeof instance.componentWillUnmount === 'function') {
    
  3167.         safelyCallComponentWillUnmount(
    
  3168.           finishedWork,
    
  3169.           finishedWork.return,
    
  3170.           instance,
    
  3171.         );
    
  3172.       }
    
  3173. 
    
  3174.       recursivelyTraverseDisappearLayoutEffects(finishedWork);
    
  3175.       break;
    
  3176.     }
    
  3177.     case HostHoistable:
    
  3178.     case HostSingleton:
    
  3179.     case HostComponent: {
    
  3180.       // TODO (Offscreen) Check: flags & RefStatic
    
  3181.       safelyDetachRef(finishedWork, finishedWork.return);
    
  3182. 
    
  3183.       recursivelyTraverseDisappearLayoutEffects(finishedWork);
    
  3184.       break;
    
  3185.     }
    
  3186.     case OffscreenComponent: {
    
  3187.       // TODO (Offscreen) Check: flags & RefStatic
    
  3188.       safelyDetachRef(finishedWork, finishedWork.return);
    
  3189. 
    
  3190.       const isHidden = finishedWork.memoizedState !== null;
    
  3191.       if (isHidden) {
    
  3192.         // Nested Offscreen tree is already hidden. Don't disappear
    
  3193.         // its effects.
    
  3194.       } else {
    
  3195.         recursivelyTraverseDisappearLayoutEffects(finishedWork);
    
  3196.       }
    
  3197.       break;
    
  3198.     }
    
  3199.     default: {
    
  3200.       recursivelyTraverseDisappearLayoutEffects(finishedWork);
    
  3201.       break;
    
  3202.     }
    
  3203.   }
    
  3204. }
    
  3205. 
    
  3206. function recursivelyTraverseDisappearLayoutEffects(parentFiber: Fiber) {
    
  3207.   // TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
    
  3208.   let child = parentFiber.child;
    
  3209.   while (child !== null) {
    
  3210.     disappearLayoutEffects(child);
    
  3211.     child = child.sibling;
    
  3212.   }
    
  3213. }
    
  3214. 
    
  3215. export function reappearLayoutEffects(
    
  3216.   finishedRoot: FiberRoot,
    
  3217.   current: Fiber | null,
    
  3218.   finishedWork: Fiber,
    
  3219.   // This function visits both newly finished work and nodes that were re-used
    
  3220.   // from a previously committed tree. We cannot check non-static flags if the
    
  3221.   // node was reused.
    
  3222.   includeWorkInProgressEffects: boolean,
    
  3223. ) {
    
  3224.   // Turn on layout effects in a tree that previously disappeared.
    
  3225.   const flags = finishedWork.flags;
    
  3226.   switch (finishedWork.tag) {
    
  3227.     case FunctionComponent:
    
  3228.     case ForwardRef:
    
  3229.     case SimpleMemoComponent: {
    
  3230.       recursivelyTraverseReappearLayoutEffects(
    
  3231.         finishedRoot,
    
  3232.         finishedWork,
    
  3233.         includeWorkInProgressEffects,
    
  3234.       );
    
  3235.       // TODO: Check flags & LayoutStatic
    
  3236.       commitHookLayoutEffects(finishedWork, HookLayout);
    
  3237.       break;
    
  3238.     }
    
  3239.     case ClassComponent: {
    
  3240.       recursivelyTraverseReappearLayoutEffects(
    
  3241.         finishedRoot,
    
  3242.         finishedWork,
    
  3243.         includeWorkInProgressEffects,
    
  3244.       );
    
  3245. 
    
  3246.       // TODO: Check for LayoutStatic flag
    
  3247.       const instance = finishedWork.stateNode;
    
  3248.       if (typeof instance.componentDidMount === 'function') {
    
  3249.         try {
    
  3250.           instance.componentDidMount();
    
  3251.         } catch (error) {
    
  3252.           captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  3253.         }
    
  3254.       }
    
  3255. 
    
  3256.       // Commit any callbacks that would have fired while the component
    
  3257.       // was hidden.
    
  3258.       const updateQueue: UpdateQueue<mixed> | null =
    
  3259.         (finishedWork.updateQueue: any);
    
  3260.       if (updateQueue !== null) {
    
  3261.         commitHiddenCallbacks(updateQueue, instance);
    
  3262.       }
    
  3263. 
    
  3264.       // If this is newly finished work, check for setState callbacks
    
  3265.       if (includeWorkInProgressEffects && flags & Callback) {
    
  3266.         commitClassCallbacks(finishedWork);
    
  3267.       }
    
  3268. 
    
  3269.       // TODO: Check flags & RefStatic
    
  3270.       safelyAttachRef(finishedWork, finishedWork.return);
    
  3271.       break;
    
  3272.     }
    
  3273.     // Unlike commitLayoutEffectsOnFiber, we don't need to handle HostRoot
    
  3274.     // because this function only visits nodes that are inside an
    
  3275.     // Offscreen fiber.
    
  3276.     // case HostRoot: {
    
  3277.     //  ...
    
  3278.     // }
    
  3279.     case HostHoistable:
    
  3280.     case HostSingleton:
    
  3281.     case HostComponent: {
    
  3282.       recursivelyTraverseReappearLayoutEffects(
    
  3283.         finishedRoot,
    
  3284.         finishedWork,
    
  3285.         includeWorkInProgressEffects,
    
  3286.       );
    
  3287. 
    
  3288.       // Renderers may schedule work to be done after host components are mounted
    
  3289.       // (eg DOM renderer may schedule auto-focus for inputs and form controls).
    
  3290.       // These effects should only be committed when components are first mounted,
    
  3291.       // aka when there is no current/alternate.
    
  3292.       if (includeWorkInProgressEffects && current === null && flags & Update) {
    
  3293.         commitHostComponentMount(finishedWork);
    
  3294.       }
    
  3295. 
    
  3296.       // TODO: Check flags & Ref
    
  3297.       safelyAttachRef(finishedWork, finishedWork.return);
    
  3298.       break;
    
  3299.     }
    
  3300.     case Profiler: {
    
  3301.       recursivelyTraverseReappearLayoutEffects(
    
  3302.         finishedRoot,
    
  3303.         finishedWork,
    
  3304.         includeWorkInProgressEffects,
    
  3305.       );
    
  3306.       // TODO: Figure out how Profiler updates should work with Offscreen
    
  3307.       if (includeWorkInProgressEffects && flags & Update) {
    
  3308.         commitProfilerUpdate(finishedWork, current);
    
  3309.       }
    
  3310.       break;
    
  3311.     }
    
  3312.     case SuspenseComponent: {
    
  3313.       recursivelyTraverseReappearLayoutEffects(
    
  3314.         finishedRoot,
    
  3315.         finishedWork,
    
  3316.         includeWorkInProgressEffects,
    
  3317.       );
    
  3318. 
    
  3319.       // TODO: Figure out how Suspense hydration callbacks should work
    
  3320.       // with Offscreen.
    
  3321.       if (includeWorkInProgressEffects && flags & Update) {
    
  3322.         commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
    
  3323.       }
    
  3324.       break;
    
  3325.     }
    
  3326.     case OffscreenComponent: {
    
  3327.       const offscreenState: OffscreenState = finishedWork.memoizedState;
    
  3328.       const isHidden = offscreenState !== null;
    
  3329.       if (isHidden) {
    
  3330.         // Nested Offscreen tree is still hidden. Don't re-appear its effects.
    
  3331.       } else {
    
  3332.         recursivelyTraverseReappearLayoutEffects(
    
  3333.           finishedRoot,
    
  3334.           finishedWork,
    
  3335.           includeWorkInProgressEffects,
    
  3336.         );
    
  3337.       }
    
  3338.       // TODO: Check flags & Ref
    
  3339.       safelyAttachRef(finishedWork, finishedWork.return);
    
  3340.       break;
    
  3341.     }
    
  3342.     default: {
    
  3343.       recursivelyTraverseReappearLayoutEffects(
    
  3344.         finishedRoot,
    
  3345.         finishedWork,
    
  3346.         includeWorkInProgressEffects,
    
  3347.       );
    
  3348.       break;
    
  3349.     }
    
  3350.   }
    
  3351. }
    
  3352. 
    
  3353. function recursivelyTraverseReappearLayoutEffects(
    
  3354.   finishedRoot: FiberRoot,
    
  3355.   parentFiber: Fiber,
    
  3356.   includeWorkInProgressEffects: boolean,
    
  3357. ) {
    
  3358.   // This function visits both newly finished work and nodes that were re-used
    
  3359.   // from a previously committed tree. We cannot check non-static flags if the
    
  3360.   // node was reused.
    
  3361.   const childShouldIncludeWorkInProgressEffects =
    
  3362.     includeWorkInProgressEffects &&
    
  3363.     (parentFiber.subtreeFlags & LayoutMask) !== NoFlags;
    
  3364. 
    
  3365.   // TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
    
  3366.   const prevDebugFiber = getCurrentDebugFiberInDEV();
    
  3367.   let child = parentFiber.child;
    
  3368.   while (child !== null) {
    
  3369.     const current = child.alternate;
    
  3370.     reappearLayoutEffects(
    
  3371.       finishedRoot,
    
  3372.       current,
    
  3373.       child,
    
  3374.       childShouldIncludeWorkInProgressEffects,
    
  3375.     );
    
  3376.     child = child.sibling;
    
  3377.   }
    
  3378.   setCurrentDebugFiberInDEV(prevDebugFiber);
    
  3379. }
    
  3380. 
    
  3381. function commitHookPassiveMountEffects(
    
  3382.   finishedWork: Fiber,
    
  3383.   hookFlags: HookFlags,
    
  3384. ) {
    
  3385.   if (shouldProfile(finishedWork)) {
    
  3386.     startPassiveEffectTimer();
    
  3387.     try {
    
  3388.       commitHookEffectListMount(hookFlags, finishedWork);
    
  3389.     } catch (error) {
    
  3390.       captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  3391.     }
    
  3392.     recordPassiveEffectDuration(finishedWork);
    
  3393.   } else {
    
  3394.     try {
    
  3395.       commitHookEffectListMount(hookFlags, finishedWork);
    
  3396.     } catch (error) {
    
  3397.       captureCommitPhaseError(finishedWork, finishedWork.return, error);
    
  3398.     }
    
  3399.   }
    
  3400. }
    
  3401. 
    
  3402. function commitOffscreenPassiveMountEffects(
    
  3403.   current: Fiber | null,
    
  3404.   finishedWork: Fiber,
    
  3405.   instance: OffscreenInstance,
    
  3406. ) {
    
  3407.   if (enableCache) {
    
  3408.     let previousCache: Cache | null = null;
    
  3409.     if (
    
  3410.       current !== null &&
    
  3411.       current.memoizedState !== null &&
    
  3412.       current.memoizedState.cachePool !== null
    
  3413.     ) {
    
  3414.       previousCache = current.memoizedState.cachePool.pool;
    
  3415.     }
    
  3416.     let nextCache: Cache | null = null;
    
  3417.     if (
    
  3418.       finishedWork.memoizedState !== null &&
    
  3419.       finishedWork.memoizedState.cachePool !== null
    
  3420.     ) {
    
  3421.       nextCache = finishedWork.memoizedState.cachePool.pool;
    
  3422.     }
    
  3423.     // Retain/release the cache used for pending (suspended) nodes.
    
  3424.     // Note that this is only reached in the non-suspended/visible case:
    
  3425.     // when the content is suspended/hidden, the retain/release occurs
    
  3426.     // via the parent Suspense component (see case above).
    
  3427.     if (nextCache !== previousCache) {
    
  3428.       if (nextCache != null) {
    
  3429.         retainCache(nextCache);
    
  3430.       }
    
  3431.       if (previousCache != null) {
    
  3432.         releaseCache(previousCache);
    
  3433.       }
    
  3434.     }
    
  3435.   }
    
  3436. 
    
  3437.   if (enableTransitionTracing) {
    
  3438.     // TODO: Pre-rendering should not be counted as part of a transition. We
    
  3439.     // may add separate logs for pre-rendering, but it's not part of the
    
  3440.     // primary metrics.
    
  3441.     const offscreenState: OffscreenState = finishedWork.memoizedState;
    
  3442.     const queue: OffscreenQueue | null = (finishedWork.updateQueue: any);
    
  3443. 
    
  3444.     const isHidden = offscreenState !== null;
    
  3445.     if (queue !== null) {
    
  3446.       if (isHidden) {
    
  3447.         const transitions = queue.transitions;
    
  3448.         if (transitions !== null) {
    
  3449.           transitions.forEach(transition => {
    
  3450.             // Add all the transitions saved in the update queue during
    
  3451.             // the render phase (ie the transitions associated with this boundary)
    
  3452.             // into the transitions set.
    
  3453.             if (instance._transitions === null) {
    
  3454.               instance._transitions = new Set();
    
  3455.             }
    
  3456.             instance._transitions.add(transition);
    
  3457.           });
    
  3458.         }
    
  3459. 
    
  3460.         const markerInstances = queue.markerInstances;
    
  3461.         if (markerInstances !== null) {
    
  3462.           markerInstances.forEach(markerInstance => {
    
  3463.             const markerTransitions = markerInstance.transitions;
    
  3464.             // There should only be a few tracing marker transitions because
    
  3465.             // they should be only associated with the transition that
    
  3466.             // caused them
    
  3467.             if (markerTransitions !== null) {
    
  3468.               markerTransitions.forEach(transition => {
    
  3469.                 if (instance._transitions === null) {
    
  3470.                   instance._transitions = new Set();
    
  3471.                 } else if (instance._transitions.has(transition)) {
    
  3472.                   if (markerInstance.pendingBoundaries === null) {
    
  3473.                     markerInstance.pendingBoundaries = new Map();
    
  3474.                   }
    
  3475.                   if (instance._pendingMarkers === null) {
    
  3476.                     instance._pendingMarkers = new Set();
    
  3477.                   }
    
  3478. 
    
  3479.                   instance._pendingMarkers.add(markerInstance);
    
  3480.                 }
    
  3481.               });
    
  3482.             }
    
  3483.           });
    
  3484.         }
    
  3485.       }
    
  3486. 
    
  3487.       finishedWork.updateQueue = null;
    
  3488.     }
    
  3489. 
    
  3490.     commitTransitionProgress(finishedWork);
    
  3491. 
    
  3492.     // TODO: Refactor this into an if/else branch
    
  3493.     if (!isHidden) {
    
  3494.       instance._transitions = null;
    
  3495.       instance._pendingMarkers = null;
    
  3496.     }
    
  3497.   }
    
  3498. }
    
  3499. 
    
  3500. function commitCachePassiveMountEffect(
    
  3501.   current: Fiber | null,
    
  3502.   finishedWork: Fiber,
    
  3503. ) {
    
  3504.   if (enableCache) {
    
  3505.     let previousCache: Cache | null = null;
    
  3506.     if (finishedWork.alternate !== null) {
    
  3507.       previousCache = finishedWork.alternate.memoizedState.cache;
    
  3508.     }
    
  3509.     const nextCache = finishedWork.memoizedState.cache;
    
  3510.     // Retain/release the cache. In theory the cache component
    
  3511.     // could be "borrowing" a cache instance owned by some parent,
    
  3512.     // in which case we could avoid retaining/releasing. But it
    
  3513.     // is non-trivial to determine when that is the case, so we
    
  3514.     // always retain/release.
    
  3515.     if (nextCache !== previousCache) {
    
  3516.       retainCache(nextCache);
    
  3517.       if (previousCache != null) {
    
  3518.         releaseCache(previousCache);
    
  3519.       }
    
  3520.     }
    
  3521.   }
    
  3522. }
    
  3523. 
    
  3524. function commitTracingMarkerPassiveMountEffect(finishedWork: Fiber) {
    
  3525.   // Get the transitions that were initiatized during the render
    
  3526.   // and add a start transition callback for each of them
    
  3527.   // We will only call this on initial mount of the tracing marker
    
  3528.   // only if there are no suspense children
    
  3529.   const instance = finishedWork.stateNode;
    
  3530.   if (instance.transitions !== null && instance.pendingBoundaries === null) {
    
  3531.     addMarkerCompleteCallbackToPendingTransition(
    
  3532.       finishedWork.memoizedProps.name,
    
  3533.       instance.transitions,
    
  3534.     );
    
  3535.     instance.transitions = null;
    
  3536.     instance.pendingBoundaries = null;
    
  3537.     instance.aborts = null;
    
  3538.     instance.name = null;
    
  3539.   }
    
  3540. }
    
  3541. 
    
  3542. export function commitPassiveMountEffects(
    
  3543.   root: FiberRoot,
    
  3544.   finishedWork: Fiber,
    
  3545.   committedLanes: Lanes,
    
  3546.   committedTransitions: Array<Transition> | null,
    
  3547. ): void {
    
  3548.   setCurrentDebugFiberInDEV(finishedWork);
    
  3549.   commitPassiveMountOnFiber(
    
  3550.     root,
    
  3551.     finishedWork,
    
  3552.     committedLanes,
    
  3553.     committedTransitions,
    
  3554.   );
    
  3555.   resetCurrentDebugFiberInDEV();
    
  3556. }
    
  3557. 
    
  3558. function recursivelyTraversePassiveMountEffects(
    
  3559.   root: FiberRoot,
    
  3560.   parentFiber: Fiber,
    
  3561.   committedLanes: Lanes,
    
  3562.   committedTransitions: Array<Transition> | null,
    
  3563. ) {
    
  3564.   const prevDebugFiber = getCurrentDebugFiberInDEV();
    
  3565.   if (parentFiber.subtreeFlags & PassiveMask) {
    
  3566.     let child = parentFiber.child;
    
  3567.     while (child !== null) {
    
  3568.       setCurrentDebugFiberInDEV(child);
    
  3569.       commitPassiveMountOnFiber(
    
  3570.         root,
    
  3571.         child,
    
  3572.         committedLanes,
    
  3573.         committedTransitions,
    
  3574.       );
    
  3575.       child = child.sibling;
    
  3576.     }
    
  3577.   }
    
  3578.   setCurrentDebugFiberInDEV(prevDebugFiber);
    
  3579. }
    
  3580. 
    
  3581. function commitPassiveMountOnFiber(
    
  3582.   finishedRoot: FiberRoot,
    
  3583.   finishedWork: Fiber,
    
  3584.   committedLanes: Lanes,
    
  3585.   committedTransitions: Array<Transition> | null,
    
  3586. ): void {
    
  3587.   // When updating this function, also update reconnectPassiveEffects, which does
    
  3588.   // most of the same things when an offscreen tree goes from hidden -> visible,
    
  3589.   // or when toggling effects inside a hidden tree.
    
  3590.   const flags = finishedWork.flags;
    
  3591.   switch (finishedWork.tag) {
    
  3592.     case FunctionComponent:
    
  3593.     case ForwardRef:
    
  3594.     case SimpleMemoComponent: {
    
  3595.       recursivelyTraversePassiveMountEffects(
    
  3596.         finishedRoot,
    
  3597.         finishedWork,
    
  3598.         committedLanes,
    
  3599.         committedTransitions,
    
  3600.       );
    
  3601.       if (flags & Passive) {
    
  3602.         commitHookPassiveMountEffects(
    
  3603.           finishedWork,
    
  3604.           HookPassive | HookHasEffect,
    
  3605.         );
    
  3606.       }
    
  3607.       break;
    
  3608.     }
    
  3609.     case HostRoot: {
    
  3610.       recursivelyTraversePassiveMountEffects(
    
  3611.         finishedRoot,
    
  3612.         finishedWork,
    
  3613.         committedLanes,
    
  3614.         committedTransitions,
    
  3615.       );
    
  3616.       if (flags & Passive) {
    
  3617.         if (enableCache) {
    
  3618.           let previousCache: Cache | null = null;
    
  3619.           if (finishedWork.alternate !== null) {
    
  3620.             previousCache = finishedWork.alternate.memoizedState.cache;
    
  3621.           }
    
  3622.           const nextCache = finishedWork.memoizedState.cache;
    
  3623.           // Retain/release the root cache.
    
  3624.           // Note that on initial mount, previousCache and nextCache will be the same
    
  3625.           // and this retain won't occur. To counter this, we instead retain the HostRoot's
    
  3626.           // initial cache when creating the root itself (see createFiberRoot() in
    
  3627.           // ReactFiberRoot.js). Subsequent updates that change the cache are reflected
    
  3628.           // here, such that previous/next caches are retained correctly.
    
  3629.           if (nextCache !== previousCache) {
    
  3630.             retainCache(nextCache);
    
  3631.             if (previousCache != null) {
    
  3632.               releaseCache(previousCache);
    
  3633.             }
    
  3634.           }
    
  3635.         }
    
  3636. 
    
  3637.         if (enableTransitionTracing) {
    
  3638.           // Get the transitions that were initiatized during the render
    
  3639.           // and add a start transition callback for each of them
    
  3640.           const root: FiberRoot = finishedWork.stateNode;
    
  3641.           const incompleteTransitions = root.incompleteTransitions;
    
  3642.           // Initial render
    
  3643.           if (committedTransitions !== null) {
    
  3644.             committedTransitions.forEach(transition => {
    
  3645.               addTransitionStartCallbackToPendingTransition(transition);
    
  3646.             });
    
  3647. 
    
  3648.             clearTransitionsForLanes(finishedRoot, committedLanes);
    
  3649.           }
    
  3650. 
    
  3651.           incompleteTransitions.forEach((markerInstance, transition) => {
    
  3652.             const pendingBoundaries = markerInstance.pendingBoundaries;
    
  3653.             if (pendingBoundaries === null || pendingBoundaries.size === 0) {
    
  3654.               if (markerInstance.aborts === null) {
    
  3655.                 addTransitionCompleteCallbackToPendingTransition(transition);
    
  3656.               }
    
  3657.               incompleteTransitions.delete(transition);
    
  3658.             }
    
  3659.           });
    
  3660. 
    
  3661.           clearTransitionsForLanes(finishedRoot, committedLanes);
    
  3662.         }
    
  3663.       }
    
  3664.       break;
    
  3665.     }
    
  3666.     case LegacyHiddenComponent: {
    
  3667.       if (enableLegacyHidden) {
    
  3668.         recursivelyTraversePassiveMountEffects(
    
  3669.           finishedRoot,
    
  3670.           finishedWork,
    
  3671.           committedLanes,
    
  3672.           committedTransitions,
    
  3673.         );
    
  3674. 
    
  3675.         if (flags & Passive) {
    
  3676.           const current = finishedWork.alternate;
    
  3677.           const instance: OffscreenInstance = finishedWork.stateNode;
    
  3678.           commitOffscreenPassiveMountEffects(current, finishedWork, instance);
    
  3679.         }
    
  3680.       }
    
  3681.       break;
    
  3682.     }
    
  3683.     case OffscreenComponent: {
    
  3684.       // TODO: Pass `current` as argument to this function
    
  3685.       const instance: OffscreenInstance = finishedWork.stateNode;
    
  3686.       const nextState: OffscreenState | null = finishedWork.memoizedState;
    
  3687. 
    
  3688.       const isHidden = nextState !== null;
    
  3689. 
    
  3690.       if (isHidden) {
    
  3691.         if (instance._visibility & OffscreenPassiveEffectsConnected) {
    
  3692.           // The effects are currently connected. Update them.
    
  3693.           recursivelyTraversePassiveMountEffects(
    
  3694.             finishedRoot,
    
  3695.             finishedWork,
    
  3696.             committedLanes,
    
  3697.             committedTransitions,
    
  3698.           );
    
  3699.         } else {
    
  3700.           if (finishedWork.mode & ConcurrentMode) {
    
  3701.             // The effects are currently disconnected. Since the tree is hidden,
    
  3702.             // don't connect them. This also applies to the initial render.
    
  3703.             if (enableCache || enableTransitionTracing) {
    
  3704.               // "Atomic" effects are ones that need to fire on every commit,
    
  3705.               // even during pre-rendering. An example is updating the reference
    
  3706.               // count on cache instances.
    
  3707.               recursivelyTraverseAtomicPassiveEffects(
    
  3708.                 finishedRoot,
    
  3709.                 finishedWork,
    
  3710.                 committedLanes,
    
  3711.                 committedTransitions,
    
  3712.               );
    
  3713.             }
    
  3714.           } else {
    
  3715.             // Legacy Mode: Fire the effects even if the tree is hidden.
    
  3716.             instance._visibility |= OffscreenPassiveEffectsConnected;
    
  3717.             recursivelyTraversePassiveMountEffects(
    
  3718.               finishedRoot,
    
  3719.               finishedWork,
    
  3720.               committedLanes,
    
  3721.               committedTransitions,
    
  3722.             );
    
  3723.           }
    
  3724.         }
    
  3725.       } else {
    
  3726.         // Tree is visible
    
  3727.         if (instance._visibility & OffscreenPassiveEffectsConnected) {
    
  3728.           // The effects are currently connected. Update them.
    
  3729.           recursivelyTraversePassiveMountEffects(
    
  3730.             finishedRoot,
    
  3731.             finishedWork,
    
  3732.             committedLanes,
    
  3733.             committedTransitions,
    
  3734.           );
    
  3735.         } else {
    
  3736.           // The effects are currently disconnected. Reconnect them, while also
    
  3737.           // firing effects inside newly mounted trees. This also applies to
    
  3738.           // the initial render.
    
  3739.           instance._visibility |= OffscreenPassiveEffectsConnected;
    
  3740. 
    
  3741.           const includeWorkInProgressEffects =
    
  3742.             (finishedWork.subtreeFlags & PassiveMask) !== NoFlags;
    
  3743.           recursivelyTraverseReconnectPassiveEffects(
    
  3744.             finishedRoot,
    
  3745.             finishedWork,
    
  3746.             committedLanes,
    
  3747.             committedTransitions,
    
  3748.             includeWorkInProgressEffects,
    
  3749.           );
    
  3750.         }
    
  3751.       }
    
  3752. 
    
  3753.       if (flags & Passive) {
    
  3754.         const current = finishedWork.alternate;
    
  3755.         commitOffscreenPassiveMountEffects(current, finishedWork, instance);
    
  3756.       }
    
  3757.       break;
    
  3758.     }
    
  3759.     case CacheComponent: {
    
  3760.       recursivelyTraversePassiveMountEffects(
    
  3761.         finishedRoot,
    
  3762.         finishedWork,
    
  3763.         committedLanes,
    
  3764.         committedTransitions,
    
  3765.       );
    
  3766.       if (flags & Passive) {
    
  3767.         // TODO: Pass `current` as argument to this function
    
  3768.         const current = finishedWork.alternate;
    
  3769.         commitCachePassiveMountEffect(current, finishedWork);
    
  3770.       }
    
  3771.       break;
    
  3772.     }
    
  3773.     case TracingMarkerComponent: {
    
  3774.       if (enableTransitionTracing) {
    
  3775.         recursivelyTraversePassiveMountEffects(
    
  3776.           finishedRoot,
    
  3777.           finishedWork,
    
  3778.           committedLanes,
    
  3779.           committedTransitions,
    
  3780.         );
    
  3781.         if (flags & Passive) {
    
  3782.           commitTracingMarkerPassiveMountEffect(finishedWork);
    
  3783.         }
    
  3784.         break;
    
  3785.       }
    
  3786.       // Intentional fallthrough to next branch
    
  3787.     }
    
  3788.     default: {
    
  3789.       recursivelyTraversePassiveMountEffects(
    
  3790.         finishedRoot,
    
  3791.         finishedWork,
    
  3792.         committedLanes,
    
  3793.         committedTransitions,
    
  3794.       );
    
  3795.       break;
    
  3796.     }
    
  3797.   }
    
  3798. }
    
  3799. 
    
  3800. function recursivelyTraverseReconnectPassiveEffects(
    
  3801.   finishedRoot: FiberRoot,
    
  3802.   parentFiber: Fiber,
    
  3803.   committedLanes: Lanes,
    
  3804.   committedTransitions: Array<Transition> | null,
    
  3805.   includeWorkInProgressEffects: boolean,
    
  3806. ) {
    
  3807.   // This function visits both newly finished work and nodes that were re-used
    
  3808.   // from a previously committed tree. We cannot check non-static flags if the
    
  3809.   // node was reused.
    
  3810.   const childShouldIncludeWorkInProgressEffects =
    
  3811.     includeWorkInProgressEffects &&
    
  3812.     (parentFiber.subtreeFlags & PassiveMask) !== NoFlags;
    
  3813. 
    
  3814.   // TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
    
  3815.   const prevDebugFiber = getCurrentDebugFiberInDEV();
    
  3816.   let child = parentFiber.child;
    
  3817.   while (child !== null) {
    
  3818.     reconnectPassiveEffects(
    
  3819.       finishedRoot,
    
  3820.       child,
    
  3821.       committedLanes,
    
  3822.       committedTransitions,
    
  3823.       childShouldIncludeWorkInProgressEffects,
    
  3824.     );
    
  3825.     child = child.sibling;
    
  3826.   }
    
  3827.   setCurrentDebugFiberInDEV(prevDebugFiber);
    
  3828. }
    
  3829. 
    
  3830. export function reconnectPassiveEffects(
    
  3831.   finishedRoot: FiberRoot,
    
  3832.   finishedWork: Fiber,
    
  3833.   committedLanes: Lanes,
    
  3834.   committedTransitions: Array<Transition> | null,
    
  3835.   // This function visits both newly finished work and nodes that were re-used
    
  3836.   // from a previously committed tree. We cannot check non-static flags if the
    
  3837.   // node was reused.
    
  3838.   includeWorkInProgressEffects: boolean,
    
  3839. ) {
    
  3840.   const flags = finishedWork.flags;
    
  3841.   switch (finishedWork.tag) {
    
  3842.     case FunctionComponent:
    
  3843.     case ForwardRef:
    
  3844.     case SimpleMemoComponent: {
    
  3845.       recursivelyTraverseReconnectPassiveEffects(
    
  3846.         finishedRoot,
    
  3847.         finishedWork,
    
  3848.         committedLanes,
    
  3849.         committedTransitions,
    
  3850.         includeWorkInProgressEffects,
    
  3851.       );
    
  3852.       // TODO: Check for PassiveStatic flag
    
  3853.       commitHookPassiveMountEffects(finishedWork, HookPassive);
    
  3854.       break;
    
  3855.     }
    
  3856.     // Unlike commitPassiveMountOnFiber, we don't need to handle HostRoot
    
  3857.     // because this function only visits nodes that are inside an
    
  3858.     // Offscreen fiber.
    
  3859.     // case HostRoot: {
    
  3860.     //  ...
    
  3861.     // }
    
  3862.     case LegacyHiddenComponent: {
    
  3863.       if (enableLegacyHidden) {
    
  3864.         recursivelyTraverseReconnectPassiveEffects(
    
  3865.           finishedRoot,
    
  3866.           finishedWork,
    
  3867.           committedLanes,
    
  3868.           committedTransitions,
    
  3869.           includeWorkInProgressEffects,
    
  3870.         );
    
  3871. 
    
  3872.         if (includeWorkInProgressEffects && flags & Passive) {
    
  3873.           // TODO: Pass `current` as argument to this function
    
  3874.           const current: Fiber | null = finishedWork.alternate;
    
  3875.           const instance: OffscreenInstance = finishedWork.stateNode;
    
  3876.           commitOffscreenPassiveMountEffects(current, finishedWork, instance);
    
  3877.         }
    
  3878.       }
    
  3879.       break;
    
  3880.     }
    
  3881.     case OffscreenComponent: {
    
  3882.       const instance: OffscreenInstance = finishedWork.stateNode;
    
  3883.       const nextState: OffscreenState | null = finishedWork.memoizedState;
    
  3884. 
    
  3885.       const isHidden = nextState !== null;
    
  3886. 
    
  3887.       if (isHidden) {
    
  3888.         if (instance._visibility & OffscreenPassiveEffectsConnected) {
    
  3889.           // The effects are currently connected. Update them.
    
  3890.           recursivelyTraverseReconnectPassiveEffects(
    
  3891.             finishedRoot,
    
  3892.             finishedWork,
    
  3893.             committedLanes,
    
  3894.             committedTransitions,
    
  3895.             includeWorkInProgressEffects,
    
  3896.           );
    
  3897.         } else {
    
  3898.           if (finishedWork.mode & ConcurrentMode) {
    
  3899.             // The effects are currently disconnected. Since the tree is hidden,
    
  3900.             // don't connect them. This also applies to the initial render.
    
  3901.             if (enableCache || enableTransitionTracing) {
    
  3902.               // "Atomic" effects are ones that need to fire on every commit,
    
  3903.               // even during pre-rendering. An example is updating the reference
    
  3904.               // count on cache instances.
    
  3905.               recursivelyTraverseAtomicPassiveEffects(
    
  3906.                 finishedRoot,
    
  3907.                 finishedWork,
    
  3908.                 committedLanes,
    
  3909.                 committedTransitions,
    
  3910.               );
    
  3911.             }
    
  3912.           } else {
    
  3913.             // Legacy Mode: Fire the effects even if the tree is hidden.
    
  3914.             instance._visibility |= OffscreenPassiveEffectsConnected;
    
  3915.             recursivelyTraverseReconnectPassiveEffects(
    
  3916.               finishedRoot,
    
  3917.               finishedWork,
    
  3918.               committedLanes,
    
  3919.               committedTransitions,
    
  3920.               includeWorkInProgressEffects,
    
  3921.             );
    
  3922.           }
    
  3923.         }
    
  3924.       } else {
    
  3925.         // Tree is visible
    
  3926. 
    
  3927.         // Since we're already inside a reconnecting tree, it doesn't matter
    
  3928.         // whether the effects are currently connected. In either case, we'll
    
  3929.         // continue traversing the tree and firing all the effects.
    
  3930.         //
    
  3931.         // We do need to set the "connected" flag on the instance, though.
    
  3932.         instance._visibility |= OffscreenPassiveEffectsConnected;
    
  3933. 
    
  3934.         recursivelyTraverseReconnectPassiveEffects(
    
  3935.           finishedRoot,
    
  3936.           finishedWork,
    
  3937.           committedLanes,
    
  3938.           committedTransitions,
    
  3939.           includeWorkInProgressEffects,
    
  3940.         );
    
  3941.       }
    
  3942. 
    
  3943.       if (includeWorkInProgressEffects && flags & Passive) {
    
  3944.         // TODO: Pass `current` as argument to this function
    
  3945.         const current: Fiber | null = finishedWork.alternate;
    
  3946.         commitOffscreenPassiveMountEffects(current, finishedWork, instance);
    
  3947.       }
    
  3948.       break;
    
  3949.     }
    
  3950.     case CacheComponent: {
    
  3951.       recursivelyTraverseReconnectPassiveEffects(
    
  3952.         finishedRoot,
    
  3953.         finishedWork,
    
  3954.         committedLanes,
    
  3955.         committedTransitions,
    
  3956.         includeWorkInProgressEffects,
    
  3957.       );
    
  3958.       if (includeWorkInProgressEffects && flags & Passive) {
    
  3959.         // TODO: Pass `current` as argument to this function
    
  3960.         const current = finishedWork.alternate;
    
  3961.         commitCachePassiveMountEffect(current, finishedWork);
    
  3962.       }
    
  3963.       break;
    
  3964.     }
    
  3965.     case TracingMarkerComponent: {
    
  3966.       if (enableTransitionTracing) {
    
  3967.         recursivelyTraverseReconnectPassiveEffects(
    
  3968.           finishedRoot,
    
  3969.           finishedWork,
    
  3970.           committedLanes,
    
  3971.           committedTransitions,
    
  3972.           includeWorkInProgressEffects,
    
  3973.         );
    
  3974.         if (includeWorkInProgressEffects && flags & Passive) {
    
  3975.           commitTracingMarkerPassiveMountEffect(finishedWork);
    
  3976.         }
    
  3977.         break;
    
  3978.       }
    
  3979.       // Intentional fallthrough to next branch
    
  3980.     }
    
  3981.     default: {
    
  3982.       recursivelyTraverseReconnectPassiveEffects(
    
  3983.         finishedRoot,
    
  3984.         finishedWork,
    
  3985.         committedLanes,
    
  3986.         committedTransitions,
    
  3987.         includeWorkInProgressEffects,
    
  3988.       );
    
  3989.       break;
    
  3990.     }
    
  3991.   }
    
  3992. }
    
  3993. 
    
  3994. function recursivelyTraverseAtomicPassiveEffects(
    
  3995.   finishedRoot: FiberRoot,
    
  3996.   parentFiber: Fiber,
    
  3997.   committedLanes: Lanes,
    
  3998.   committedTransitions: Array<Transition> | null,
    
  3999. ) {
    
  4000.   // "Atomic" effects are ones that need to fire on every commit, even during
    
  4001.   // pre-rendering. We call this function when traversing a hidden tree whose
    
  4002.   // regular effects are currently disconnected.
    
  4003.   const prevDebugFiber = getCurrentDebugFiberInDEV();
    
  4004.   // TODO: Add special flag for atomic effects
    
  4005.   if (parentFiber.subtreeFlags & PassiveMask) {
    
  4006.     let child = parentFiber.child;
    
  4007.     while (child !== null) {
    
  4008.       setCurrentDebugFiberInDEV(child);
    
  4009.       commitAtomicPassiveEffects(
    
  4010.         finishedRoot,
    
  4011.         child,
    
  4012.         committedLanes,
    
  4013.         committedTransitions,
    
  4014.       );
    
  4015.       child = child.sibling;
    
  4016.     }
    
  4017.   }
    
  4018.   setCurrentDebugFiberInDEV(prevDebugFiber);
    
  4019. }
    
  4020. 
    
  4021. function commitAtomicPassiveEffects(
    
  4022.   finishedRoot: FiberRoot,
    
  4023.   finishedWork: Fiber,
    
  4024.   committedLanes: Lanes,
    
  4025.   committedTransitions: Array<Transition> | null,
    
  4026. ) {
    
  4027.   // "Atomic" effects are ones that need to fire on every commit, even during
    
  4028.   // pre-rendering. We call this function when traversing a hidden tree whose
    
  4029.   // regular effects are currently disconnected.
    
  4030.   const flags = finishedWork.flags;
    
  4031.   switch (finishedWork.tag) {
    
  4032.     case OffscreenComponent: {
    
  4033.       recursivelyTraverseAtomicPassiveEffects(
    
  4034.         finishedRoot,
    
  4035.         finishedWork,
    
  4036.         committedLanes,
    
  4037.         committedTransitions,
    
  4038.       );
    
  4039.       if (flags & Passive) {
    
  4040.         // TODO: Pass `current` as argument to this function
    
  4041.         const current = finishedWork.alternate;
    
  4042.         const instance: OffscreenInstance = finishedWork.stateNode;
    
  4043.         commitOffscreenPassiveMountEffects(current, finishedWork, instance);
    
  4044.       }
    
  4045.       break;
    
  4046.     }
    
  4047.     case CacheComponent: {
    
  4048.       recursivelyTraverseAtomicPassiveEffects(
    
  4049.         finishedRoot,
    
  4050.         finishedWork,
    
  4051.         committedLanes,
    
  4052.         committedTransitions,
    
  4053.       );
    
  4054.       if (flags & Passive) {
    
  4055.         // TODO: Pass `current` as argument to this function
    
  4056.         const current = finishedWork.alternate;
    
  4057.         commitCachePassiveMountEffect(current, finishedWork);
    
  4058.       }
    
  4059.       break;
    
  4060.     }
    
  4061.     default: {
    
  4062.       recursivelyTraverseAtomicPassiveEffects(
    
  4063.         finishedRoot,
    
  4064.         finishedWork,
    
  4065.         committedLanes,
    
  4066.         committedTransitions,
    
  4067.       );
    
  4068.       break;
    
  4069.     }
    
  4070.   }
    
  4071. }
    
  4072. 
    
  4073. export function commitPassiveUnmountEffects(finishedWork: Fiber): void {
    
  4074.   setCurrentDebugFiberInDEV(finishedWork);
    
  4075.   commitPassiveUnmountOnFiber(finishedWork);
    
  4076.   resetCurrentDebugFiberInDEV();
    
  4077. }
    
  4078. 
    
  4079. // If we're inside a brand new tree, or a tree that was already visible, then we
    
  4080. // should only suspend host components that have a ShouldSuspendCommit flag.
    
  4081. // Components without it haven't changed since the last commit, so we can skip
    
  4082. // over those.
    
  4083. //
    
  4084. // When we enter a tree that is being revealed (going from hidden -> visible),
    
  4085. // we need to suspend _any_ component that _may_ suspend. Even if they're
    
  4086. // already in the "current" tree. Because their visibility has changed, the
    
  4087. // browser may not have prerendered them yet. So we check the MaySuspendCommit
    
  4088. // flag instead.
    
  4089. let suspenseyCommitFlag = ShouldSuspendCommit;
    
  4090. export function accumulateSuspenseyCommit(finishedWork: Fiber): void {
    
  4091.   accumulateSuspenseyCommitOnFiber(finishedWork);
    
  4092. }
    
  4093. 
    
  4094. function recursivelyAccumulateSuspenseyCommit(parentFiber: Fiber): void {
    
  4095.   if (parentFiber.subtreeFlags & suspenseyCommitFlag) {
    
  4096.     let child = parentFiber.child;
    
  4097.     while (child !== null) {
    
  4098.       accumulateSuspenseyCommitOnFiber(child);
    
  4099.       child = child.sibling;
    
  4100.     }
    
  4101.   }
    
  4102. }
    
  4103. 
    
  4104. function accumulateSuspenseyCommitOnFiber(fiber: Fiber) {
    
  4105.   switch (fiber.tag) {
    
  4106.     case HostHoistable: {
    
  4107.       recursivelyAccumulateSuspenseyCommit(fiber);
    
  4108.       if (fiber.flags & suspenseyCommitFlag) {
    
  4109.         if (fiber.memoizedState !== null) {
    
  4110.           suspendResource(
    
  4111.             // This should always be set by visiting HostRoot first
    
  4112.             (currentHoistableRoot: any),
    
  4113.             fiber.memoizedState,
    
  4114.             fiber.memoizedProps,
    
  4115.           );
    
  4116.         } else {
    
  4117.           const type = fiber.type;
    
  4118.           const props = fiber.memoizedProps;
    
  4119.           suspendInstance(type, props);
    
  4120.         }
    
  4121.       }
    
  4122.       break;
    
  4123.     }
    
  4124.     case HostComponent: {
    
  4125.       recursivelyAccumulateSuspenseyCommit(fiber);
    
  4126.       if (fiber.flags & suspenseyCommitFlag) {
    
  4127.         const type = fiber.type;
    
  4128.         const props = fiber.memoizedProps;
    
  4129.         suspendInstance(type, props);
    
  4130.       }
    
  4131.       break;
    
  4132.     }
    
  4133.     case HostRoot:
    
  4134.     case HostPortal: {
    
  4135.       if (enableFloat && supportsResources) {
    
  4136.         const previousHoistableRoot = currentHoistableRoot;
    
  4137.         const container: Container = fiber.stateNode.containerInfo;
    
  4138.         currentHoistableRoot = getHoistableRoot(container);
    
  4139. 
    
  4140.         recursivelyAccumulateSuspenseyCommit(fiber);
    
  4141.         currentHoistableRoot = previousHoistableRoot;
    
  4142.       } else {
    
  4143.         recursivelyAccumulateSuspenseyCommit(fiber);
    
  4144.       }
    
  4145.       break;
    
  4146.     }
    
  4147.     case OffscreenComponent: {
    
  4148.       const isHidden = (fiber.memoizedState: OffscreenState | null) !== null;
    
  4149.       if (isHidden) {
    
  4150.         // Don't suspend in hidden trees
    
  4151.       } else {
    
  4152.         const current = fiber.alternate;
    
  4153.         const wasHidden =
    
  4154.           current !== null &&
    
  4155.           (current.memoizedState: OffscreenState | null) !== null;
    
  4156.         if (wasHidden) {
    
  4157.           // This tree is being revealed. Visit all newly visible suspensey
    
  4158.           // instances, even if they're in the current tree.
    
  4159.           const prevFlags = suspenseyCommitFlag;
    
  4160.           suspenseyCommitFlag = MaySuspendCommit;
    
  4161.           recursivelyAccumulateSuspenseyCommit(fiber);
    
  4162.           suspenseyCommitFlag = prevFlags;
    
  4163.         } else {
    
  4164.           recursivelyAccumulateSuspenseyCommit(fiber);
    
  4165.         }
    
  4166.       }
    
  4167.       break;
    
  4168.     }
    
  4169.     default: {
    
  4170.       recursivelyAccumulateSuspenseyCommit(fiber);
    
  4171.     }
    
  4172.   }
    
  4173. }
    
  4174. 
    
  4175. function detachAlternateSiblings(parentFiber: Fiber) {
    
  4176.   // A fiber was deleted from this parent fiber, but it's still part of the
    
  4177.   // previous (alternate) parent fiber's list of children. Because children
    
  4178.   // are a linked list, an earlier sibling that's still alive will be
    
  4179.   // connected to the deleted fiber via its `alternate`:
    
  4180.   //
    
  4181.   //   live fiber --alternate--> previous live fiber --sibling--> deleted
    
  4182.   //   fiber
    
  4183.   //
    
  4184.   // We can't disconnect `alternate` on nodes that haven't been deleted yet,
    
  4185.   // but we can disconnect the `sibling` and `child` pointers.
    
  4186. 
    
  4187.   const previousFiber = parentFiber.alternate;
    
  4188.   if (previousFiber !== null) {
    
  4189.     let detachedChild = previousFiber.child;
    
  4190.     if (detachedChild !== null) {
    
  4191.       previousFiber.child = null;
    
  4192.       do {
    
  4193.         // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  4194.         const detachedSibling = detachedChild.sibling;
    
  4195.         // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  4196.         detachedChild.sibling = null;
    
  4197.         detachedChild = detachedSibling;
    
  4198.       } while (detachedChild !== null);
    
  4199.     }
    
  4200.   }
    
  4201. }
    
  4202. 
    
  4203. function commitHookPassiveUnmountEffects(
    
  4204.   finishedWork: Fiber,
    
  4205.   nearestMountedAncestor: null | Fiber,
    
  4206.   hookFlags: HookFlags,
    
  4207. ) {
    
  4208.   if (shouldProfile(finishedWork)) {
    
  4209.     startPassiveEffectTimer();
    
  4210.     commitHookEffectListUnmount(
    
  4211.       hookFlags,
    
  4212.       finishedWork,
    
  4213.       nearestMountedAncestor,
    
  4214.     );
    
  4215.     recordPassiveEffectDuration(finishedWork);
    
  4216.   } else {
    
  4217.     commitHookEffectListUnmount(
    
  4218.       hookFlags,
    
  4219.       finishedWork,
    
  4220.       nearestMountedAncestor,
    
  4221.     );
    
  4222.   }
    
  4223. }
    
  4224. 
    
  4225. function recursivelyTraversePassiveUnmountEffects(parentFiber: Fiber): void {
    
  4226.   // Deletions effects can be scheduled on any fiber type. They need to happen
    
  4227.   // before the children effects have fired.
    
  4228.   const deletions = parentFiber.deletions;
    
  4229. 
    
  4230.   if ((parentFiber.flags & ChildDeletion) !== NoFlags) {
    
  4231.     if (deletions !== null) {
    
  4232.       for (let i = 0; i < deletions.length; i++) {
    
  4233.         const childToDelete = deletions[i];
    
  4234.         // TODO: Convert this to use recursion
    
  4235.         nextEffect = childToDelete;
    
  4236.         commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
    
  4237.           childToDelete,
    
  4238.           parentFiber,
    
  4239.         );
    
  4240.       }
    
  4241.     }
    
  4242.     detachAlternateSiblings(parentFiber);
    
  4243.   }
    
  4244. 
    
  4245.   const prevDebugFiber = getCurrentDebugFiberInDEV();
    
  4246.   // TODO: Split PassiveMask into separate masks for mount and unmount?
    
  4247.   if (parentFiber.subtreeFlags & PassiveMask) {
    
  4248.     let child = parentFiber.child;
    
  4249.     while (child !== null) {
    
  4250.       setCurrentDebugFiberInDEV(child);
    
  4251.       commitPassiveUnmountOnFiber(child);
    
  4252.       child = child.sibling;
    
  4253.     }
    
  4254.   }
    
  4255.   setCurrentDebugFiberInDEV(prevDebugFiber);
    
  4256. }
    
  4257. 
    
  4258. function commitPassiveUnmountOnFiber(finishedWork: Fiber): void {
    
  4259.   switch (finishedWork.tag) {
    
  4260.     case FunctionComponent:
    
  4261.     case ForwardRef:
    
  4262.     case SimpleMemoComponent: {
    
  4263.       recursivelyTraversePassiveUnmountEffects(finishedWork);
    
  4264.       if (finishedWork.flags & Passive) {
    
  4265.         commitHookPassiveUnmountEffects(
    
  4266.           finishedWork,
    
  4267.           finishedWork.return,
    
  4268.           HookPassive | HookHasEffect,
    
  4269.         );
    
  4270.       }
    
  4271.       break;
    
  4272.     }
    
  4273.     case OffscreenComponent: {
    
  4274.       const instance: OffscreenInstance = finishedWork.stateNode;
    
  4275.       const nextState: OffscreenState | null = finishedWork.memoizedState;
    
  4276. 
    
  4277.       const isHidden = nextState !== null;
    
  4278. 
    
  4279.       if (
    
  4280.         isHidden &&
    
  4281.         instance._visibility & OffscreenPassiveEffectsConnected &&
    
  4282.         // For backwards compatibility, don't unmount when a tree suspends. In
    
  4283.         // the future we may change this to unmount after a delay.
    
  4284.         (finishedWork.return === null ||
    
  4285.           finishedWork.return.tag !== SuspenseComponent)
    
  4286.       ) {
    
  4287.         // The effects are currently connected. Disconnect them.
    
  4288.         // TODO: Add option or heuristic to delay before disconnecting the
    
  4289.         // effects. Then if the tree reappears before the delay has elapsed, we
    
  4290.         // can skip toggling the effects entirely.
    
  4291.         instance._visibility &= ~OffscreenPassiveEffectsConnected;
    
  4292.         recursivelyTraverseDisconnectPassiveEffects(finishedWork);
    
  4293.       } else {
    
  4294.         recursivelyTraversePassiveUnmountEffects(finishedWork);
    
  4295.       }
    
  4296. 
    
  4297.       break;
    
  4298.     }
    
  4299.     default: {
    
  4300.       recursivelyTraversePassiveUnmountEffects(finishedWork);
    
  4301.       break;
    
  4302.     }
    
  4303.   }
    
  4304. }
    
  4305. 
    
  4306. function recursivelyTraverseDisconnectPassiveEffects(parentFiber: Fiber): void {
    
  4307.   // Deletions effects can be scheduled on any fiber type. They need to happen
    
  4308.   // before the children effects have fired.
    
  4309.   const deletions = parentFiber.deletions;
    
  4310. 
    
  4311.   if ((parentFiber.flags & ChildDeletion) !== NoFlags) {
    
  4312.     if (deletions !== null) {
    
  4313.       for (let i = 0; i < deletions.length; i++) {
    
  4314.         const childToDelete = deletions[i];
    
  4315.         // TODO: Convert this to use recursion
    
  4316.         nextEffect = childToDelete;
    
  4317.         commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
    
  4318.           childToDelete,
    
  4319.           parentFiber,
    
  4320.         );
    
  4321.       }
    
  4322.     }
    
  4323.     detachAlternateSiblings(parentFiber);
    
  4324.   }
    
  4325. 
    
  4326.   const prevDebugFiber = getCurrentDebugFiberInDEV();
    
  4327.   // TODO: Check PassiveStatic flag
    
  4328.   let child = parentFiber.child;
    
  4329.   while (child !== null) {
    
  4330.     setCurrentDebugFiberInDEV(child);
    
  4331.     disconnectPassiveEffect(child);
    
  4332.     child = child.sibling;
    
  4333.   }
    
  4334.   setCurrentDebugFiberInDEV(prevDebugFiber);
    
  4335. }
    
  4336. 
    
  4337. export function disconnectPassiveEffect(finishedWork: Fiber): void {
    
  4338.   switch (finishedWork.tag) {
    
  4339.     case FunctionComponent:
    
  4340.     case ForwardRef:
    
  4341.     case SimpleMemoComponent: {
    
  4342.       // TODO: Check PassiveStatic flag
    
  4343.       commitHookPassiveUnmountEffects(
    
  4344.         finishedWork,
    
  4345.         finishedWork.return,
    
  4346.         HookPassive,
    
  4347.       );
    
  4348.       // When disconnecting passive effects, we fire the effects in the same
    
  4349.       // order as during a deletiong: parent before child
    
  4350.       recursivelyTraverseDisconnectPassiveEffects(finishedWork);
    
  4351.       break;
    
  4352.     }
    
  4353.     case OffscreenComponent: {
    
  4354.       const instance: OffscreenInstance = finishedWork.stateNode;
    
  4355.       if (instance._visibility & OffscreenPassiveEffectsConnected) {
    
  4356.         instance._visibility &= ~OffscreenPassiveEffectsConnected;
    
  4357.         recursivelyTraverseDisconnectPassiveEffects(finishedWork);
    
  4358.       } else {
    
  4359.         // The effects are already disconnected.
    
  4360.       }
    
  4361.       break;
    
  4362.     }
    
  4363.     default: {
    
  4364.       recursivelyTraverseDisconnectPassiveEffects(finishedWork);
    
  4365.       break;
    
  4366.     }
    
  4367.   }
    
  4368. }
    
  4369. 
    
  4370. function commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
    
  4371.   deletedSubtreeRoot: Fiber,
    
  4372.   nearestMountedAncestor: Fiber | null,
    
  4373. ) {
    
  4374.   while (nextEffect !== null) {
    
  4375.     const fiber = nextEffect;
    
  4376. 
    
  4377.     // Deletion effects fire in parent -> child order
    
  4378.     // TODO: Check if fiber has a PassiveStatic flag
    
  4379.     setCurrentDebugFiberInDEV(fiber);
    
  4380.     commitPassiveUnmountInsideDeletedTreeOnFiber(fiber, nearestMountedAncestor);
    
  4381.     resetCurrentDebugFiberInDEV();
    
  4382. 
    
  4383.     const child = fiber.child;
    
  4384.     // TODO: Only traverse subtree if it has a PassiveStatic flag.
    
  4385.     if (child !== null) {
    
  4386.       child.return = fiber;
    
  4387.       nextEffect = child;
    
  4388.     } else {
    
  4389.       commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
    
  4390.         deletedSubtreeRoot,
    
  4391.       );
    
  4392.     }
    
  4393.   }
    
  4394. }
    
  4395. 
    
  4396. function commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
    
  4397.   deletedSubtreeRoot: Fiber,
    
  4398. ) {
    
  4399.   while (nextEffect !== null) {
    
  4400.     const fiber = nextEffect;
    
  4401.     const sibling = fiber.sibling;
    
  4402.     const returnFiber = fiber.return;
    
  4403. 
    
  4404.     // Recursively traverse the entire deleted tree and clean up fiber fields.
    
  4405.     // This is more aggressive than ideal, and the long term goal is to only
    
  4406.     // have to detach the deleted tree at the root.
    
  4407.     detachFiberAfterEffects(fiber);
    
  4408.     if (fiber === deletedSubtreeRoot) {
    
  4409.       nextEffect = null;
    
  4410.       return;
    
  4411.     }
    
  4412. 
    
  4413.     if (sibling !== null) {
    
  4414.       sibling.return = returnFiber;
    
  4415.       nextEffect = sibling;
    
  4416.       return;
    
  4417.     }
    
  4418. 
    
  4419.     nextEffect = returnFiber;
    
  4420.   }
    
  4421. }
    
  4422. 
    
  4423. function commitPassiveUnmountInsideDeletedTreeOnFiber(
    
  4424.   current: Fiber,
    
  4425.   nearestMountedAncestor: Fiber | null,
    
  4426. ): void {
    
  4427.   switch (current.tag) {
    
  4428.     case FunctionComponent:
    
  4429.     case ForwardRef:
    
  4430.     case SimpleMemoComponent: {
    
  4431.       commitHookPassiveUnmountEffects(
    
  4432.         current,
    
  4433.         nearestMountedAncestor,
    
  4434.         HookPassive,
    
  4435.       );
    
  4436.       break;
    
  4437.     }
    
  4438.     // TODO: run passive unmount effects when unmounting a root.
    
  4439.     // Because passive unmount effects are not currently run,
    
  4440.     // the cache instance owned by the root will never be freed.
    
  4441.     // When effects are run, the cache should be freed here:
    
  4442.     // case HostRoot: {
    
  4443.     //   if (enableCache) {
    
  4444.     //     const cache = current.memoizedState.cache;
    
  4445.     //     releaseCache(cache);
    
  4446.     //   }
    
  4447.     //   break;
    
  4448.     // }
    
  4449.     case LegacyHiddenComponent:
    
  4450.     case OffscreenComponent: {
    
  4451.       if (enableCache) {
    
  4452.         if (
    
  4453.           current.memoizedState !== null &&
    
  4454.           current.memoizedState.cachePool !== null
    
  4455.         ) {
    
  4456.           const cache: Cache = current.memoizedState.cachePool.pool;
    
  4457.           // Retain/release the cache used for pending (suspended) nodes.
    
  4458.           // Note that this is only reached in the non-suspended/visible case:
    
  4459.           // when the content is suspended/hidden, the retain/release occurs
    
  4460.           // via the parent Suspense component (see case above).
    
  4461.           if (cache != null) {
    
  4462.             retainCache(cache);
    
  4463.           }
    
  4464.         }
    
  4465.       }
    
  4466.       break;
    
  4467.     }
    
  4468.     case SuspenseComponent: {
    
  4469.       if (enableTransitionTracing) {
    
  4470.         // We need to mark this fiber's parents as deleted
    
  4471.         const offscreenFiber: Fiber = (current.child: any);
    
  4472.         const instance: OffscreenInstance = offscreenFiber.stateNode;
    
  4473.         const transitions = instance._transitions;
    
  4474.         if (transitions !== null) {
    
  4475.           const abortReason = {
    
  4476.             reason: 'suspense',
    
  4477.             name: current.memoizedProps.unstable_name || null,
    
  4478.           };
    
  4479.           if (
    
  4480.             current.memoizedState === null ||
    
  4481.             current.memoizedState.dehydrated === null
    
  4482.           ) {
    
  4483.             abortParentMarkerTransitionsForDeletedFiber(
    
  4484.               offscreenFiber,
    
  4485.               abortReason,
    
  4486.               transitions,
    
  4487.               instance,
    
  4488.               true,
    
  4489.             );
    
  4490. 
    
  4491.             if (nearestMountedAncestor !== null) {
    
  4492.               abortParentMarkerTransitionsForDeletedFiber(
    
  4493.                 nearestMountedAncestor,
    
  4494.                 abortReason,
    
  4495.                 transitions,
    
  4496.                 instance,
    
  4497.                 false,
    
  4498.               );
    
  4499.             }
    
  4500.           }
    
  4501.         }
    
  4502.       }
    
  4503.       break;
    
  4504.     }
    
  4505.     case CacheComponent: {
    
  4506.       if (enableCache) {
    
  4507.         const cache = current.memoizedState.cache;
    
  4508.         releaseCache(cache);
    
  4509.       }
    
  4510.       break;
    
  4511.     }
    
  4512.     case TracingMarkerComponent: {
    
  4513.       if (enableTransitionTracing) {
    
  4514.         // We need to mark this fiber's parents as deleted
    
  4515.         const instance: TracingMarkerInstance = current.stateNode;
    
  4516.         const transitions = instance.transitions;
    
  4517.         if (transitions !== null) {
    
  4518.           const abortReason = {
    
  4519.             reason: 'marker',
    
  4520.             name: current.memoizedProps.name,
    
  4521.           };
    
  4522.           abortParentMarkerTransitionsForDeletedFiber(
    
  4523.             current,
    
  4524.             abortReason,
    
  4525.             transitions,
    
  4526.             null,
    
  4527.             true,
    
  4528.           );
    
  4529. 
    
  4530.           if (nearestMountedAncestor !== null) {
    
  4531.             abortParentMarkerTransitionsForDeletedFiber(
    
  4532.               nearestMountedAncestor,
    
  4533.               abortReason,
    
  4534.               transitions,
    
  4535.               null,
    
  4536.               false,
    
  4537.             );
    
  4538.           }
    
  4539.         }
    
  4540.       }
    
  4541.       break;
    
  4542.     }
    
  4543.   }
    
  4544. }
    
  4545. 
    
  4546. function invokeLayoutEffectMountInDEV(fiber: Fiber): void {
    
  4547.   if (__DEV__) {
    
  4548.     // We don't need to re-check StrictEffectsMode here.
    
  4549.     // This function is only called if that check has already passed.
    
  4550.     switch (fiber.tag) {
    
  4551.       case FunctionComponent:
    
  4552.       case ForwardRef:
    
  4553.       case SimpleMemoComponent: {
    
  4554.         try {
    
  4555.           commitHookEffectListMount(HookLayout | HookHasEffect, fiber);
    
  4556.         } catch (error) {
    
  4557.           captureCommitPhaseError(fiber, fiber.return, error);
    
  4558.         }
    
  4559.         break;
    
  4560.       }
    
  4561.       case ClassComponent: {
    
  4562.         const instance = fiber.stateNode;
    
  4563.         if (typeof instance.componentDidMount === 'function') {
    
  4564.           try {
    
  4565.             instance.componentDidMount();
    
  4566.           } catch (error) {
    
  4567.             captureCommitPhaseError(fiber, fiber.return, error);
    
  4568.           }
    
  4569.         }
    
  4570.         break;
    
  4571.       }
    
  4572.     }
    
  4573.   }
    
  4574. }
    
  4575. 
    
  4576. function invokePassiveEffectMountInDEV(fiber: Fiber): void {
    
  4577.   if (__DEV__) {
    
  4578.     // We don't need to re-check StrictEffectsMode here.
    
  4579.     // This function is only called if that check has already passed.
    
  4580.     switch (fiber.tag) {
    
  4581.       case FunctionComponent:
    
  4582.       case ForwardRef:
    
  4583.       case SimpleMemoComponent: {
    
  4584.         try {
    
  4585.           commitHookEffectListMount(HookPassive | HookHasEffect, fiber);
    
  4586.         } catch (error) {
    
  4587.           captureCommitPhaseError(fiber, fiber.return, error);
    
  4588.         }
    
  4589.         break;
    
  4590.       }
    
  4591.     }
    
  4592.   }
    
  4593. }
    
  4594. 
    
  4595. function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void {
    
  4596.   if (__DEV__) {
    
  4597.     // We don't need to re-check StrictEffectsMode here.
    
  4598.     // This function is only called if that check has already passed.
    
  4599.     switch (fiber.tag) {
    
  4600.       case FunctionComponent:
    
  4601.       case ForwardRef:
    
  4602.       case SimpleMemoComponent: {
    
  4603.         try {
    
  4604.           commitHookEffectListUnmount(
    
  4605.             HookLayout | HookHasEffect,
    
  4606.             fiber,
    
  4607.             fiber.return,
    
  4608.           );
    
  4609.         } catch (error) {
    
  4610.           captureCommitPhaseError(fiber, fiber.return, error);
    
  4611.         }
    
  4612.         break;
    
  4613.       }
    
  4614.       case ClassComponent: {
    
  4615.         const instance = fiber.stateNode;
    
  4616.         if (typeof instance.componentWillUnmount === 'function') {
    
  4617.           safelyCallComponentWillUnmount(fiber, fiber.return, instance);
    
  4618.         }
    
  4619.         break;
    
  4620.       }
    
  4621.     }
    
  4622.   }
    
  4623. }
    
  4624. 
    
  4625. function invokePassiveEffectUnmountInDEV(fiber: Fiber): void {
    
  4626.   if (__DEV__) {
    
  4627.     // We don't need to re-check StrictEffectsMode here.
    
  4628.     // This function is only called if that check has already passed.
    
  4629.     switch (fiber.tag) {
    
  4630.       case FunctionComponent:
    
  4631.       case ForwardRef:
    
  4632.       case SimpleMemoComponent: {
    
  4633.         try {
    
  4634.           commitHookEffectListUnmount(
    
  4635.             HookPassive | HookHasEffect,
    
  4636.             fiber,
    
  4637.             fiber.return,
    
  4638.           );
    
  4639.         } catch (error) {
    
  4640.           captureCommitPhaseError(fiber, fiber.return, error);
    
  4641.         }
    
  4642.       }
    
  4643.     }
    
  4644.   }
    
  4645. }
    
  4646. 
    
  4647. export {
    
  4648.   commitPlacement,
    
  4649.   commitAttachRef,
    
  4650.   invokeLayoutEffectMountInDEV,
    
  4651.   invokeLayoutEffectUnmountInDEV,
    
  4652.   invokePassiveEffectMountInDEV,
    
  4653.   invokePassiveEffectUnmountInDEV,
    
  4654. };