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 {Fiber, FiberRoot} from './ReactInternalTypes';
    
  11. import type {RootState} from './ReactFiberRoot';
    
  12. import type {Lanes, Lane} from './ReactFiberLane';
    
  13. import type {ReactScopeInstance, ReactContext} from 'shared/ReactTypes';
    
  14. import type {
    
  15.   Instance,
    
  16.   Type,
    
  17.   Props,
    
  18.   Container,
    
  19.   ChildSet,
    
  20.   Resource,
    
  21. } from './ReactFiberConfig';
    
  22. import type {
    
  23.   SuspenseState,
    
  24.   SuspenseListRenderState,
    
  25.   RetryQueue,
    
  26. } from './ReactFiberSuspenseComponent';
    
  27. import type {
    
  28.   OffscreenState,
    
  29.   OffscreenQueue,
    
  30. } from './ReactFiberActivityComponent';
    
  31. import {isOffscreenManual} from './ReactFiberActivityComponent';
    
  32. import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent';
    
  33. import type {Cache} from './ReactFiberCacheComponent';
    
  34. import {
    
  35.   enableLegacyHidden,
    
  36.   enableHostSingletons,
    
  37.   enableSuspenseCallback,
    
  38.   enableScopeAPI,
    
  39.   enableProfilerTimer,
    
  40.   enableCache,
    
  41.   enableTransitionTracing,
    
  42.   enableFloat,
    
  43.   passChildrenWhenCloningPersistedNodes,
    
  44. } from 'shared/ReactFeatureFlags';
    
  45. 
    
  46. import {now} from './Scheduler';
    
  47. 
    
  48. import {
    
  49.   IndeterminateComponent,
    
  50.   FunctionComponent,
    
  51.   ClassComponent,
    
  52.   HostRoot,
    
  53.   HostComponent,
    
  54.   HostHoistable,
    
  55.   HostSingleton,
    
  56.   HostText,
    
  57.   HostPortal,
    
  58.   ContextProvider,
    
  59.   ContextConsumer,
    
  60.   ForwardRef,
    
  61.   Fragment,
    
  62.   Mode,
    
  63.   Profiler,
    
  64.   SuspenseComponent,
    
  65.   SuspenseListComponent,
    
  66.   MemoComponent,
    
  67.   SimpleMemoComponent,
    
  68.   LazyComponent,
    
  69.   IncompleteClassComponent,
    
  70.   ScopeComponent,
    
  71.   OffscreenComponent,
    
  72.   LegacyHiddenComponent,
    
  73.   CacheComponent,
    
  74.   TracingMarkerComponent,
    
  75. } from './ReactWorkTags';
    
  76. import {NoMode, ConcurrentMode, ProfileMode} from './ReactTypeOfMode';
    
  77. import {
    
  78.   Ref,
    
  79.   RefStatic,
    
  80.   Placement,
    
  81.   Update,
    
  82.   Visibility,
    
  83.   NoFlags,
    
  84.   DidCapture,
    
  85.   Snapshot,
    
  86.   ChildDeletion,
    
  87.   StaticMask,
    
  88.   MutationMask,
    
  89.   Passive,
    
  90.   ForceClientRender,
    
  91.   MaySuspendCommit,
    
  92.   ScheduleRetry,
    
  93.   ShouldSuspendCommit,
    
  94. } from './ReactFiberFlags';
    
  95. 
    
  96. import {
    
  97.   createInstance,
    
  98.   createTextInstance,
    
  99.   resolveSingletonInstance,
    
  100.   appendInitialChild,
    
  101.   finalizeInitialChildren,
    
  102.   supportsMutation,
    
  103.   supportsPersistence,
    
  104.   supportsResources,
    
  105.   supportsSingletons,
    
  106.   cloneInstance,
    
  107.   cloneHiddenInstance,
    
  108.   cloneHiddenTextInstance,
    
  109.   createContainerChildSet,
    
  110.   appendChildToContainerChildSet,
    
  111.   finalizeContainerChildren,
    
  112.   preparePortalMount,
    
  113.   prepareScopeUpdate,
    
  114.   maySuspendCommit,
    
  115.   mayResourceSuspendCommit,
    
  116.   preloadInstance,
    
  117.   preloadResource,
    
  118. } from './ReactFiberConfig';
    
  119. import {
    
  120.   getRootHostContainer,
    
  121.   popHostContext,
    
  122.   getHostContext,
    
  123.   popHostContainer,
    
  124. } from './ReactFiberHostContext';
    
  125. import {
    
  126.   suspenseStackCursor,
    
  127.   popSuspenseListContext,
    
  128.   popSuspenseHandler,
    
  129.   pushSuspenseListContext,
    
  130.   setShallowSuspenseListContext,
    
  131.   ForceSuspenseFallback,
    
  132.   setDefaultShallowSuspenseListContext,
    
  133. } from './ReactFiberSuspenseContext';
    
  134. import {popHiddenContext} from './ReactFiberHiddenContext';
    
  135. import {findFirstSuspended} from './ReactFiberSuspenseComponent';
    
  136. import {
    
  137.   isContextProvider as isLegacyContextProvider,
    
  138.   popContext as popLegacyContext,
    
  139.   popTopLevelContextObject as popTopLevelLegacyContextObject,
    
  140. } from './ReactFiberContext';
    
  141. import {popProvider} from './ReactFiberNewContext';
    
  142. import {
    
  143.   prepareToHydrateHostInstance,
    
  144.   prepareToHydrateHostTextInstance,
    
  145.   prepareToHydrateHostSuspenseInstance,
    
  146.   warnIfUnhydratedTailNodes,
    
  147.   popHydrationState,
    
  148.   resetHydrationState,
    
  149.   getIsHydrating,
    
  150.   hasUnhydratedTailNodes,
    
  151.   upgradeHydrationErrorsToRecoverable,
    
  152. } from './ReactFiberHydrationContext';
    
  153. import {
    
  154.   renderHasNotSuspendedYet,
    
  155.   getRenderTargetTime,
    
  156.   getWorkInProgressTransitions,
    
  157.   shouldRemainOnPreviousScreen,
    
  158.   getWorkInProgressRootRenderLanes,
    
  159. } from './ReactFiberWorkLoop';
    
  160. import {
    
  161.   OffscreenLane,
    
  162.   SomeRetryLane,
    
  163.   NoLanes,
    
  164.   includesSomeLane,
    
  165.   mergeLanes,
    
  166.   claimNextRetryLane,
    
  167.   includesOnlyNonUrgentLanes,
    
  168. } from './ReactFiberLane';
    
  169. import {resetChildFibers} from './ReactChildFiber';
    
  170. import {createScopeInstance} from './ReactFiberScope';
    
  171. import {transferActualDuration} from './ReactProfilerTimer';
    
  172. import {popCacheProvider} from './ReactFiberCacheComponent';
    
  173. import {popTreeContext} from './ReactFiberTreeContext';
    
  174. import {popRootTransition, popTransition} from './ReactFiberTransition';
    
  175. import {
    
  176.   popMarkerInstance,
    
  177.   popRootMarkerInstance,
    
  178. } from './ReactFiberTracingMarkerComponent';
    
  179. import {suspendCommit} from './ReactFiberThenable';
    
  180. 
    
  181. function markUpdate(workInProgress: Fiber) {
    
  182.   // Tag the fiber with an update effect. This turns a Placement into
    
  183.   // a PlacementAndUpdate.
    
  184.   workInProgress.flags |= Update;
    
  185. }
    
  186. 
    
  187. function markRef(workInProgress: Fiber) {
    
  188.   workInProgress.flags |= Ref | RefStatic;
    
  189. }
    
  190. 
    
  191. function hadNoMutationsEffects(current: null | Fiber, completedWork: Fiber) {
    
  192.   const didBailout = current !== null && current.child === completedWork.child;
    
  193.   if (didBailout) {
    
  194.     return true;
    
  195.   }
    
  196. 
    
  197.   if ((completedWork.flags & ChildDeletion) !== NoFlags) {
    
  198.     return false;
    
  199.   }
    
  200. 
    
  201.   // TODO: If we move the `hadNoMutationsEffects` call after `bubbleProperties`
    
  202.   // then we only have to check the `completedWork.subtreeFlags`.
    
  203.   let child = completedWork.child;
    
  204.   while (child !== null) {
    
  205.     if (
    
  206.       (child.flags & MutationMask) !== NoFlags ||
    
  207.       (child.subtreeFlags & MutationMask) !== NoFlags
    
  208.     ) {
    
  209.       return false;
    
  210.     }
    
  211.     child = child.sibling;
    
  212.   }
    
  213.   return true;
    
  214. }
    
  215. 
    
  216. function appendAllChildren(
    
  217.   parent: Instance,
    
  218.   workInProgress: Fiber,
    
  219.   needsVisibilityToggle: boolean,
    
  220.   isHidden: boolean,
    
  221. ) {
    
  222.   if (supportsMutation) {
    
  223.     // We only have the top Fiber that was created but we need recurse down its
    
  224.     // children to find all the terminal nodes.
    
  225.     let node = workInProgress.child;
    
  226.     while (node !== null) {
    
  227.       if (node.tag === HostComponent || node.tag === HostText) {
    
  228.         appendInitialChild(parent, node.stateNode);
    
  229.       } else if (
    
  230.         node.tag === HostPortal ||
    
  231.         (enableHostSingletons && supportsSingletons
    
  232.           ? node.tag === HostSingleton
    
  233.           : false)
    
  234.       ) {
    
  235.         // If we have a portal child, then we don't want to traverse
    
  236.         // down its children. Instead, we'll get insertions from each child in
    
  237.         // the portal directly.
    
  238.         // If we have a HostSingleton it will be placed independently
    
  239.       } else if (node.child !== null) {
    
  240.         node.child.return = node;
    
  241.         node = node.child;
    
  242.         continue;
    
  243.       }
    
  244.       if (node === workInProgress) {
    
  245.         return;
    
  246.       }
    
  247.       // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  248.       while (node.sibling === null) {
    
  249.         // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  250.         if (node.return === null || node.return === workInProgress) {
    
  251.           return;
    
  252.         }
    
  253.         node = node.return;
    
  254.       }
    
  255.       // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  256.       node.sibling.return = node.return;
    
  257.       node = node.sibling;
    
  258.     }
    
  259.   } else if (supportsPersistence) {
    
  260.     // We only have the top Fiber that was created but we need recurse down its
    
  261.     // children to find all the terminal nodes.
    
  262.     let node = workInProgress.child;
    
  263.     while (node !== null) {
    
  264.       if (node.tag === HostComponent) {
    
  265.         let instance = node.stateNode;
    
  266.         if (needsVisibilityToggle && isHidden) {
    
  267.           // This child is inside a timed out tree. Hide it.
    
  268.           const props = node.memoizedProps;
    
  269.           const type = node.type;
    
  270.           instance = cloneHiddenInstance(instance, type, props);
    
  271.         }
    
  272.         appendInitialChild(parent, instance);
    
  273.       } else if (node.tag === HostText) {
    
  274.         let instance = node.stateNode;
    
  275.         if (needsVisibilityToggle && isHidden) {
    
  276.           // This child is inside a timed out tree. Hide it.
    
  277.           const text = node.memoizedProps;
    
  278.           instance = cloneHiddenTextInstance(instance, text);
    
  279.         }
    
  280.         appendInitialChild(parent, instance);
    
  281.       } else if (node.tag === HostPortal) {
    
  282.         // If we have a portal child, then we don't want to traverse
    
  283.         // down its children. Instead, we'll get insertions from each child in
    
  284.         // the portal directly.
    
  285.       } else if (
    
  286.         node.tag === OffscreenComponent &&
    
  287.         node.memoizedState !== null
    
  288.       ) {
    
  289.         // The children in this boundary are hidden. Toggle their visibility
    
  290.         // before appending.
    
  291.         const child = node.child;
    
  292.         if (child !== null) {
    
  293.           child.return = node;
    
  294.         }
    
  295.         appendAllChildren(
    
  296.           parent,
    
  297.           node,
    
  298.           /* needsVisibilityToggle */ true,
    
  299.           /* isHidden */ true,
    
  300.         );
    
  301.       } else if (node.child !== null) {
    
  302.         node.child.return = node;
    
  303.         node = node.child;
    
  304.         continue;
    
  305.       }
    
  306.       node = (node: Fiber);
    
  307.       if (node === workInProgress) {
    
  308.         return;
    
  309.       }
    
  310.       // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  311.       while (node.sibling === null) {
    
  312.         // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  313.         if (node.return === null || node.return === workInProgress) {
    
  314.           return;
    
  315.         }
    
  316.         node = node.return;
    
  317.       }
    
  318.       // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  319.       node.sibling.return = node.return;
    
  320.       node = node.sibling;
    
  321.     }
    
  322.   }
    
  323. }
    
  324. 
    
  325. // An unfortunate fork of appendAllChildren because we have two different parent types.
    
  326. function appendAllChildrenToContainer(
    
  327.   containerChildSet: ChildSet,
    
  328.   workInProgress: Fiber,
    
  329.   needsVisibilityToggle: boolean,
    
  330.   isHidden: boolean,
    
  331. ) {
    
  332.   if (supportsPersistence) {
    
  333.     // We only have the top Fiber that was created but we need recurse down its
    
  334.     // children to find all the terminal nodes.
    
  335.     let node = workInProgress.child;
    
  336.     while (node !== null) {
    
  337.       // eslint-disable-next-line no-labels
    
  338.       if (node.tag === HostComponent) {
    
  339.         let instance = node.stateNode;
    
  340.         if (needsVisibilityToggle && isHidden) {
    
  341.           // This child is inside a timed out tree. Hide it.
    
  342.           const props = node.memoizedProps;
    
  343.           const type = node.type;
    
  344.           instance = cloneHiddenInstance(instance, type, props);
    
  345.         }
    
  346.         appendChildToContainerChildSet(containerChildSet, instance);
    
  347.       } else if (node.tag === HostText) {
    
  348.         let instance = node.stateNode;
    
  349.         if (needsVisibilityToggle && isHidden) {
    
  350.           // This child is inside a timed out tree. Hide it.
    
  351.           const text = node.memoizedProps;
    
  352.           instance = cloneHiddenTextInstance(instance, text);
    
  353.         }
    
  354.         appendChildToContainerChildSet(containerChildSet, instance);
    
  355.       } else if (node.tag === HostPortal) {
    
  356.         // If we have a portal child, then we don't want to traverse
    
  357.         // down its children. Instead, we'll get insertions from each child in
    
  358.         // the portal directly.
    
  359.       } else if (
    
  360.         node.tag === OffscreenComponent &&
    
  361.         node.memoizedState !== null
    
  362.       ) {
    
  363.         // The children in this boundary are hidden. Toggle their visibility
    
  364.         // before appending.
    
  365.         const child = node.child;
    
  366.         if (child !== null) {
    
  367.           child.return = node;
    
  368.         }
    
  369.         // If Offscreen is not in manual mode, detached tree is hidden from user space.
    
  370.         const _needsVisibilityToggle = !isOffscreenManual(node);
    
  371.         appendAllChildrenToContainer(
    
  372.           containerChildSet,
    
  373.           node,
    
  374.           /* needsVisibilityToggle */ _needsVisibilityToggle,
    
  375.           /* isHidden */ true,
    
  376.         );
    
  377.       } else if (node.child !== null) {
    
  378.         node.child.return = node;
    
  379.         node = node.child;
    
  380.         continue;
    
  381.       }
    
  382.       node = (node: Fiber);
    
  383.       if (node === workInProgress) {
    
  384.         return;
    
  385.       }
    
  386.       // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  387.       while (node.sibling === null) {
    
  388.         // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  389.         if (node.return === null || node.return === workInProgress) {
    
  390.           return;
    
  391.         }
    
  392.         node = node.return;
    
  393.       }
    
  394.       // $FlowFixMe[incompatible-use] found when upgrading Flow
    
  395.       node.sibling.return = node.return;
    
  396.       node = node.sibling;
    
  397.     }
    
  398.   }
    
  399. }
    
  400. 
    
  401. function updateHostContainer(current: null | Fiber, workInProgress: Fiber) {
    
  402.   if (supportsPersistence) {
    
  403.     const portalOrRoot: {
    
  404.       containerInfo: Container,
    
  405.       pendingChildren: ChildSet,
    
  406.       ...
    
  407.     } = workInProgress.stateNode;
    
  408.     const childrenUnchanged = hadNoMutationsEffects(current, workInProgress);
    
  409.     if (childrenUnchanged) {
    
  410.       // No changes, just reuse the existing instance.
    
  411.     } else {
    
  412.       const container = portalOrRoot.containerInfo;
    
  413.       const newChildSet = createContainerChildSet();
    
  414.       // If children might have changed, we have to add them all to the set.
    
  415.       appendAllChildrenToContainer(
    
  416.         newChildSet,
    
  417.         workInProgress,
    
  418.         /* needsVisibilityToggle */ false,
    
  419.         /* isHidden */ false,
    
  420.       );
    
  421.       portalOrRoot.pendingChildren = newChildSet;
    
  422.       // Schedule an update on the container to swap out the container.
    
  423.       markUpdate(workInProgress);
    
  424.       finalizeContainerChildren(container, newChildSet);
    
  425.     }
    
  426.   }
    
  427. }
    
  428. 
    
  429. function updateHostComponent(
    
  430.   current: Fiber,
    
  431.   workInProgress: Fiber,
    
  432.   type: Type,
    
  433.   newProps: Props,
    
  434.   renderLanes: Lanes,
    
  435. ) {
    
  436.   if (supportsMutation) {
    
  437.     // If we have an alternate, that means this is an update and we need to
    
  438.     // schedule a side-effect to do the updates.
    
  439.     const oldProps = current.memoizedProps;
    
  440.     if (oldProps === newProps) {
    
  441.       // In mutation mode, this is sufficient for a bailout because
    
  442.       // we won't touch this node even if children changed.
    
  443.       return;
    
  444.     }
    
  445. 
    
  446.     markUpdate(workInProgress);
    
  447.   } else if (supportsPersistence) {
    
  448.     const currentInstance = current.stateNode;
    
  449.     const oldProps = current.memoizedProps;
    
  450.     // If there are no effects associated with this node, then none of our children had any updates.
    
  451.     // This guarantees that we can reuse all of them.
    
  452.     const childrenUnchanged = hadNoMutationsEffects(current, workInProgress);
    
  453.     if (childrenUnchanged && oldProps === newProps) {
    
  454.       // No changes, just reuse the existing instance.
    
  455.       // Note that this might release a previous clone.
    
  456.       workInProgress.stateNode = currentInstance;
    
  457.       return;
    
  458.     }
    
  459.     const currentHostContext = getHostContext();
    
  460. 
    
  461.     let newChildSet = null;
    
  462.     if (!childrenUnchanged && passChildrenWhenCloningPersistedNodes) {
    
  463.       newChildSet = createContainerChildSet();
    
  464.       // If children might have changed, we have to add them all to the set.
    
  465.       appendAllChildrenToContainer(
    
  466.         newChildSet,
    
  467.         workInProgress,
    
  468.         /* needsVisibilityToggle */ false,
    
  469.         /* isHidden */ false,
    
  470.       );
    
  471.     }
    
  472. 
    
  473.     const newInstance = cloneInstance(
    
  474.       currentInstance,
    
  475.       type,
    
  476.       oldProps,
    
  477.       newProps,
    
  478.       childrenUnchanged,
    
  479.       newChildSet,
    
  480.     );
    
  481.     if (newInstance === currentInstance) {
    
  482.       // No changes, just reuse the existing instance.
    
  483.       // Note that this might release a previous clone.
    
  484.       workInProgress.stateNode = currentInstance;
    
  485.       return;
    
  486.     }
    
  487. 
    
  488.     // Certain renderers require commit-time effects for initial mount.
    
  489.     // (eg DOM renderer supports auto-focus for certain elements).
    
  490.     // Make sure such renderers get scheduled for later work.
    
  491.     if (
    
  492.       finalizeInitialChildren(newInstance, type, newProps, currentHostContext)
    
  493.     ) {
    
  494.       markUpdate(workInProgress);
    
  495.     }
    
  496.     workInProgress.stateNode = newInstance;
    
  497.     if (childrenUnchanged) {
    
  498.       // If there are no other effects in this tree, we need to flag this node as having one.
    
  499.       // Even though we're not going to use it for anything.
    
  500.       // Otherwise parents won't know that there are new children to propagate upwards.
    
  501.       markUpdate(workInProgress);
    
  502.     } else if (!passChildrenWhenCloningPersistedNodes) {
    
  503.       // If children might have changed, we have to add them all to the set.
    
  504.       appendAllChildren(
    
  505.         newInstance,
    
  506.         workInProgress,
    
  507.         /* needsVisibilityToggle */ false,
    
  508.         /* isHidden */ false,
    
  509.       );
    
  510.     }
    
  511.   }
    
  512. }
    
  513. 
    
  514. // This function must be called at the very end of the complete phase, because
    
  515. // it might throw to suspend, and if the resource immediately loads, the work
    
  516. // loop will resume rendering as if the work-in-progress completed. So it must
    
  517. // fully complete.
    
  518. // TODO: This should ideally move to begin phase, but currently the instance is
    
  519. // not created until the complete phase. For our existing use cases, host nodes
    
  520. // that suspend don't have children, so it doesn't matter. But that might not
    
  521. // always be true in the future.
    
  522. function preloadInstanceAndSuspendIfNeeded(
    
  523.   workInProgress: Fiber,
    
  524.   type: Type,
    
  525.   props: Props,
    
  526.   renderLanes: Lanes,
    
  527. ) {
    
  528.   if (!maySuspendCommit(type, props)) {
    
  529.     // If this flag was set previously, we can remove it. The flag
    
  530.     // represents whether this particular set of props might ever need to
    
  531.     // suspend. The safest thing to do is for maySuspendCommit to always
    
  532.     // return true, but if the renderer is reasonably confident that the
    
  533.     // underlying resource won't be evicted, it can return false as a
    
  534.     // performance optimization.
    
  535.     workInProgress.flags &= ~MaySuspendCommit;
    
  536.     return;
    
  537.   }
    
  538. 
    
  539.   // Mark this fiber with a flag. This gets set on all host instances
    
  540.   // that might possibly suspend, even if they don't need to suspend
    
  541.   // currently. We use this when revealing a prerendered tree, because
    
  542.   // even though the tree has "mounted", its resources might not have
    
  543.   // loaded yet.
    
  544.   workInProgress.flags |= MaySuspendCommit;
    
  545. 
    
  546.   // Check if we're rendering at a "non-urgent" priority. This is the same
    
  547.   // check that `useDeferredValue` does to determine whether it needs to
    
  548.   // defer. This is partly for gradual adoption purposes (i.e. shouldn't start
    
  549.   // suspending until you opt in with startTransition or Suspense) but it
    
  550.   // also happens to be the desired behavior for the concrete use cases we've
    
  551.   // thought of so far, like CSS loading, fonts, images, etc.
    
  552.   //
    
  553.   // We check the "root" render lanes here rather than the "subtree" render
    
  554.   // because during a retry or offscreen prerender, the "subtree" render
    
  555.   // lanes may include additional "base" lanes that were deferred during
    
  556.   // a previous render.
    
  557.   // TODO: We may decide to expose a way to force a fallback even during a
    
  558.   // sync update.
    
  559.   const rootRenderLanes = getWorkInProgressRootRenderLanes();
    
  560.   if (!includesOnlyNonUrgentLanes(rootRenderLanes)) {
    
  561.     // This is an urgent render. Don't suspend or show a fallback. Also,
    
  562.     // there's no need to preload, because we're going to commit this
    
  563.     // synchronously anyway.
    
  564.     // TODO: Could there be benefit to preloading even during a synchronous
    
  565.     // render? The main thread will be blocked until the commit phase, but
    
  566.     // maybe the browser would be able to start loading off thread anyway?
    
  567.     // Likely a micro-optimization either way because typically new content
    
  568.     // is loaded during a transition, not an urgent render.
    
  569.   } else {
    
  570.     // Preload the instance
    
  571.     const isReady = preloadInstance(type, props);
    
  572.     if (!isReady) {
    
  573.       if (shouldRemainOnPreviousScreen()) {
    
  574.         // It's OK to suspend. Mark the fiber so we know to suspend before the
    
  575.         // commit phase. Then continue rendering.
    
  576.         workInProgress.flags |= ShouldSuspendCommit;
    
  577.       } else {
    
  578.         // Trigger a fallback rather than block the render.
    
  579.         suspendCommit();
    
  580.       }
    
  581.     }
    
  582.   }
    
  583. }
    
  584. 
    
  585. function preloadResourceAndSuspendIfNeeded(
    
  586.   workInProgress: Fiber,
    
  587.   resource: Resource,
    
  588.   type: Type,
    
  589.   props: Props,
    
  590.   renderLanes: Lanes,
    
  591. ) {
    
  592.   // This is a fork of preloadInstanceAndSuspendIfNeeded, but for resources.
    
  593.   if (!mayResourceSuspendCommit(resource)) {
    
  594.     workInProgress.flags &= ~MaySuspendCommit;
    
  595.     return;
    
  596.   }
    
  597. 
    
  598.   workInProgress.flags |= MaySuspendCommit;
    
  599. 
    
  600.   const rootRenderLanes = getWorkInProgressRootRenderLanes();
    
  601.   if (!includesOnlyNonUrgentLanes(rootRenderLanes)) {
    
  602.     // This is an urgent render. Don't suspend or show a fallback.
    
  603.   } else {
    
  604.     const isReady = preloadResource(resource);
    
  605.     if (!isReady) {
    
  606.       if (shouldRemainOnPreviousScreen()) {
    
  607.         workInProgress.flags |= ShouldSuspendCommit;
    
  608.       } else {
    
  609.         suspendCommit();
    
  610.       }
    
  611.     }
    
  612.   }
    
  613. }
    
  614. 
    
  615. function scheduleRetryEffect(
    
  616.   workInProgress: Fiber,
    
  617.   retryQueue: RetryQueue | null,
    
  618. ) {
    
  619.   const wakeables = retryQueue;
    
  620.   if (wakeables !== null) {
    
  621.     // Schedule an effect to attach a retry listener to the promise.
    
  622.     // TODO: Move to passive phase
    
  623.     workInProgress.flags |= Update;
    
  624.   } else {
    
  625.     // This boundary suspended, but no wakeables were added to the retry
    
  626.     // queue. Check if the renderer suspended commit. If so, this means
    
  627.     // that once the fallback is committed, we can immediately retry
    
  628.     // rendering again, because rendering wasn't actually blocked. Only
    
  629.     // the commit phase.
    
  630.     // TODO: Consider a model where we always schedule an immediate retry, even
    
  631.     // for normal Suspense. That way the retry can partially render up to the
    
  632.     // first thing that suspends.
    
  633.     if (workInProgress.flags & ScheduleRetry) {
    
  634.       const retryLane =
    
  635.         // TODO: This check should probably be moved into claimNextRetryLane
    
  636.         // I also suspect that we need some further consolidation of offscreen
    
  637.         // and retry lanes.
    
  638.         workInProgress.tag !== OffscreenComponent
    
  639.           ? claimNextRetryLane()
    
  640.           : OffscreenLane;
    
  641.       workInProgress.lanes = mergeLanes(workInProgress.lanes, retryLane);
    
  642.     }
    
  643.   }
    
  644. }
    
  645. 
    
  646. function updateHostText(
    
  647.   current: Fiber,
    
  648.   workInProgress: Fiber,
    
  649.   oldText: string,
    
  650.   newText: string,
    
  651. ) {
    
  652.   if (supportsMutation) {
    
  653.     // If the text differs, mark it as an update. All the work in done in commitWork.
    
  654.     if (oldText !== newText) {
    
  655.       markUpdate(workInProgress);
    
  656.     }
    
  657.   } else if (supportsPersistence) {
    
  658.     if (oldText !== newText) {
    
  659.       // If the text content differs, we'll create a new text instance for it.
    
  660.       const rootContainerInstance = getRootHostContainer();
    
  661.       const currentHostContext = getHostContext();
    
  662.       workInProgress.stateNode = createTextInstance(
    
  663.         newText,
    
  664.         rootContainerInstance,
    
  665.         currentHostContext,
    
  666.         workInProgress,
    
  667.       );
    
  668.       // We'll have to mark it as having an effect, even though we won't use the effect for anything.
    
  669.       // This lets the parents know that at least one of their children has changed.
    
  670.       markUpdate(workInProgress);
    
  671.     } else {
    
  672.       workInProgress.stateNode = current.stateNode;
    
  673.     }
    
  674.   }
    
  675. }
    
  676. 
    
  677. function cutOffTailIfNeeded(
    
  678.   renderState: SuspenseListRenderState,
    
  679.   hasRenderedATailFallback: boolean,
    
  680. ) {
    
  681.   if (getIsHydrating()) {
    
  682.     // If we're hydrating, we should consume as many items as we can
    
  683.     // so we don't leave any behind.
    
  684.     return;
    
  685.   }
    
  686.   switch (renderState.tailMode) {
    
  687.     case 'hidden': {
    
  688.       // Any insertions at the end of the tail list after this point
    
  689.       // should be invisible. If there are already mounted boundaries
    
  690.       // anything before them are not considered for collapsing.
    
  691.       // Therefore we need to go through the whole tail to find if
    
  692.       // there are any.
    
  693.       let tailNode = renderState.tail;
    
  694.       let lastTailNode = null;
    
  695.       while (tailNode !== null) {
    
  696.         if (tailNode.alternate !== null) {
    
  697.           lastTailNode = tailNode;
    
  698.         }
    
  699.         tailNode = tailNode.sibling;
    
  700.       }
    
  701.       // Next we're simply going to delete all insertions after the
    
  702.       // last rendered item.
    
  703.       if (lastTailNode === null) {
    
  704.         // All remaining items in the tail are insertions.
    
  705.         renderState.tail = null;
    
  706.       } else {
    
  707.         // Detach the insertion after the last node that was already
    
  708.         // inserted.
    
  709.         lastTailNode.sibling = null;
    
  710.       }
    
  711.       break;
    
  712.     }
    
  713.     case 'collapsed': {
    
  714.       // Any insertions at the end of the tail list after this point
    
  715.       // should be invisible. If there are already mounted boundaries
    
  716.       // anything before them are not considered for collapsing.
    
  717.       // Therefore we need to go through the whole tail to find if
    
  718.       // there are any.
    
  719.       let tailNode = renderState.tail;
    
  720.       let lastTailNode = null;
    
  721.       while (tailNode !== null) {
    
  722.         if (tailNode.alternate !== null) {
    
  723.           lastTailNode = tailNode;
    
  724.         }
    
  725.         tailNode = tailNode.sibling;
    
  726.       }
    
  727.       // Next we're simply going to delete all insertions after the
    
  728.       // last rendered item.
    
  729.       if (lastTailNode === null) {
    
  730.         // All remaining items in the tail are insertions.
    
  731.         if (!hasRenderedATailFallback && renderState.tail !== null) {
    
  732.           // We suspended during the head. We want to show at least one
    
  733.           // row at the tail. So we'll keep on and cut off the rest.
    
  734.           renderState.tail.sibling = null;
    
  735.         } else {
    
  736.           renderState.tail = null;
    
  737.         }
    
  738.       } else {
    
  739.         // Detach the insertion after the last node that was already
    
  740.         // inserted.
    
  741.         lastTailNode.sibling = null;
    
  742.       }
    
  743.       break;
    
  744.     }
    
  745.   }
    
  746. }
    
  747. 
    
  748. function bubbleProperties(completedWork: Fiber) {
    
  749.   const didBailout =
    
  750.     completedWork.alternate !== null &&
    
  751.     completedWork.alternate.child === completedWork.child;
    
  752. 
    
  753.   let newChildLanes = NoLanes;
    
  754.   let subtreeFlags = NoFlags;
    
  755. 
    
  756.   if (!didBailout) {
    
  757.     // Bubble up the earliest expiration time.
    
  758.     if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {
    
  759.       // In profiling mode, resetChildExpirationTime is also used to reset
    
  760.       // profiler durations.
    
  761.       let actualDuration = completedWork.actualDuration;
    
  762.       let treeBaseDuration = ((completedWork.selfBaseDuration: any): number);
    
  763. 
    
  764.       let child = completedWork.child;
    
  765.       while (child !== null) {
    
  766.         newChildLanes = mergeLanes(
    
  767.           newChildLanes,
    
  768.           mergeLanes(child.lanes, child.childLanes),
    
  769.         );
    
  770. 
    
  771.         subtreeFlags |= child.subtreeFlags;
    
  772.         subtreeFlags |= child.flags;
    
  773. 
    
  774.         // When a fiber is cloned, its actualDuration is reset to 0. This value will
    
  775.         // only be updated if work is done on the fiber (i.e. it doesn't bailout).
    
  776.         // When work is done, it should bubble to the parent's actualDuration. If
    
  777.         // the fiber has not been cloned though, (meaning no work was done), then
    
  778.         // this value will reflect the amount of time spent working on a previous
    
  779.         // render. In that case it should not bubble. We determine whether it was
    
  780.         // cloned by comparing the child pointer.
    
  781.         // $FlowFixMe[unsafe-addition] addition with possible null/undefined value
    
  782.         actualDuration += child.actualDuration;
    
  783. 
    
  784.         // $FlowFixMe[unsafe-addition] addition with possible null/undefined value
    
  785.         treeBaseDuration += child.treeBaseDuration;
    
  786.         child = child.sibling;
    
  787.       }
    
  788. 
    
  789.       completedWork.actualDuration = actualDuration;
    
  790.       completedWork.treeBaseDuration = treeBaseDuration;
    
  791.     } else {
    
  792.       let child = completedWork.child;
    
  793.       while (child !== null) {
    
  794.         newChildLanes = mergeLanes(
    
  795.           newChildLanes,
    
  796.           mergeLanes(child.lanes, child.childLanes),
    
  797.         );
    
  798. 
    
  799.         subtreeFlags |= child.subtreeFlags;
    
  800.         subtreeFlags |= child.flags;
    
  801. 
    
  802.         // Update the return pointer so the tree is consistent. This is a code
    
  803.         // smell because it assumes the commit phase is never concurrent with
    
  804.         // the render phase. Will address during refactor to alternate model.
    
  805.         child.return = completedWork;
    
  806. 
    
  807.         child = child.sibling;
    
  808.       }
    
  809.     }
    
  810. 
    
  811.     completedWork.subtreeFlags |= subtreeFlags;
    
  812.   } else {
    
  813.     // Bubble up the earliest expiration time.
    
  814.     if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {
    
  815.       // In profiling mode, resetChildExpirationTime is also used to reset
    
  816.       // profiler durations.
    
  817.       let treeBaseDuration = ((completedWork.selfBaseDuration: any): number);
    
  818. 
    
  819.       let child = completedWork.child;
    
  820.       while (child !== null) {
    
  821.         newChildLanes = mergeLanes(
    
  822.           newChildLanes,
    
  823.           mergeLanes(child.lanes, child.childLanes),
    
  824.         );
    
  825. 
    
  826.         // "Static" flags share the lifetime of the fiber/hook they belong to,
    
  827.         // so we should bubble those up even during a bailout. All the other
    
  828.         // flags have a lifetime only of a single render + commit, so we should
    
  829.         // ignore them.
    
  830.         subtreeFlags |= child.subtreeFlags & StaticMask;
    
  831.         subtreeFlags |= child.flags & StaticMask;
    
  832. 
    
  833.         // $FlowFixMe[unsafe-addition] addition with possible null/undefined value
    
  834.         treeBaseDuration += child.treeBaseDuration;
    
  835.         child = child.sibling;
    
  836.       }
    
  837. 
    
  838.       completedWork.treeBaseDuration = treeBaseDuration;
    
  839.     } else {
    
  840.       let child = completedWork.child;
    
  841.       while (child !== null) {
    
  842.         newChildLanes = mergeLanes(
    
  843.           newChildLanes,
    
  844.           mergeLanes(child.lanes, child.childLanes),
    
  845.         );
    
  846. 
    
  847.         // "Static" flags share the lifetime of the fiber/hook they belong to,
    
  848.         // so we should bubble those up even during a bailout. All the other
    
  849.         // flags have a lifetime only of a single render + commit, so we should
    
  850.         // ignore them.
    
  851.         subtreeFlags |= child.subtreeFlags & StaticMask;
    
  852.         subtreeFlags |= child.flags & StaticMask;
    
  853. 
    
  854.         // Update the return pointer so the tree is consistent. This is a code
    
  855.         // smell because it assumes the commit phase is never concurrent with
    
  856.         // the render phase. Will address during refactor to alternate model.
    
  857.         child.return = completedWork;
    
  858. 
    
  859.         child = child.sibling;
    
  860.       }
    
  861.     }
    
  862. 
    
  863.     completedWork.subtreeFlags |= subtreeFlags;
    
  864.   }
    
  865. 
    
  866.   completedWork.childLanes = newChildLanes;
    
  867. 
    
  868.   return didBailout;
    
  869. }
    
  870. 
    
  871. function completeDehydratedSuspenseBoundary(
    
  872.   current: Fiber | null,
    
  873.   workInProgress: Fiber,
    
  874.   nextState: SuspenseState | null,
    
  875. ): boolean {
    
  876.   if (
    
  877.     hasUnhydratedTailNodes() &&
    
  878.     (workInProgress.mode & ConcurrentMode) !== NoMode &&
    
  879.     (workInProgress.flags & DidCapture) === NoFlags
    
  880.   ) {
    
  881.     warnIfUnhydratedTailNodes(workInProgress);
    
  882.     resetHydrationState();
    
  883.     workInProgress.flags |= ForceClientRender | DidCapture;
    
  884. 
    
  885.     return false;
    
  886.   }
    
  887. 
    
  888.   const wasHydrated = popHydrationState(workInProgress);
    
  889. 
    
  890.   if (nextState !== null && nextState.dehydrated !== null) {
    
  891.     // We might be inside a hydration state the first time we're picking up this
    
  892.     // Suspense boundary, and also after we've reentered it for further hydration.
    
  893.     if (current === null) {
    
  894.       if (!wasHydrated) {
    
  895.         throw new Error(
    
  896.           'A dehydrated suspense component was completed without a hydrated node. ' +
    
  897.             'This is probably a bug in React.',
    
  898.         );
    
  899.       }
    
  900.       prepareToHydrateHostSuspenseInstance(workInProgress);
    
  901.       bubbleProperties(workInProgress);
    
  902.       if (enableProfilerTimer) {
    
  903.         if ((workInProgress.mode & ProfileMode) !== NoMode) {
    
  904.           const isTimedOutSuspense = nextState !== null;
    
  905.           if (isTimedOutSuspense) {
    
  906.             // Don't count time spent in a timed out Suspense subtree as part of the base duration.
    
  907.             const primaryChildFragment = workInProgress.child;
    
  908.             if (primaryChildFragment !== null) {
    
  909.               // $FlowFixMe[unsafe-arithmetic] Flow doesn't support type casting in combination with the -= operator
    
  910.               workInProgress.treeBaseDuration -=
    
  911.                 ((primaryChildFragment.treeBaseDuration: any): number);
    
  912.             }
    
  913.           }
    
  914.         }
    
  915.       }
    
  916.       return false;
    
  917.     } else {
    
  918.       // We might have reentered this boundary to hydrate it. If so, we need to reset the hydration
    
  919.       // state since we're now exiting out of it. popHydrationState doesn't do that for us.
    
  920.       resetHydrationState();
    
  921.       if ((workInProgress.flags & DidCapture) === NoFlags) {
    
  922.         // This boundary did not suspend so it's now hydrated and unsuspended.
    
  923.         workInProgress.memoizedState = null;
    
  924.       }
    
  925.       // If nothing suspended, we need to schedule an effect to mark this boundary
    
  926.       // as having hydrated so events know that they're free to be invoked.
    
  927.       // It's also a signal to replay events and the suspense callback.
    
  928.       // If something suspended, schedule an effect to attach retry listeners.
    
  929.       // So we might as well always mark this.
    
  930.       workInProgress.flags |= Update;
    
  931.       bubbleProperties(workInProgress);
    
  932.       if (enableProfilerTimer) {
    
  933.         if ((workInProgress.mode & ProfileMode) !== NoMode) {
    
  934.           const isTimedOutSuspense = nextState !== null;
    
  935.           if (isTimedOutSuspense) {
    
  936.             // Don't count time spent in a timed out Suspense subtree as part of the base duration.
    
  937.             const primaryChildFragment = workInProgress.child;
    
  938.             if (primaryChildFragment !== null) {
    
  939.               // $FlowFixMe[unsafe-arithmetic] Flow doesn't support type casting in combination with the -= operator
    
  940.               workInProgress.treeBaseDuration -=
    
  941.                 ((primaryChildFragment.treeBaseDuration: any): number);
    
  942.             }
    
  943.           }
    
  944.         }
    
  945.       }
    
  946.       return false;
    
  947.     }
    
  948.   } else {
    
  949.     // Successfully completed this tree. If this was a forced client render,
    
  950.     // there may have been recoverable errors during first hydration
    
  951.     // attempt. If so, add them to a queue so we can log them in the
    
  952.     // commit phase.
    
  953.     upgradeHydrationErrorsToRecoverable();
    
  954. 
    
  955.     // Fall through to normal Suspense path
    
  956.     return true;
    
  957.   }
    
  958. }
    
  959. 
    
  960. function completeWork(
    
  961.   current: Fiber | null,
    
  962.   workInProgress: Fiber,
    
  963.   renderLanes: Lanes,
    
  964. ): Fiber | null {
    
  965.   const newProps = workInProgress.pendingProps;
    
  966.   // Note: This intentionally doesn't check if we're hydrating because comparing
    
  967.   // to the current tree provider fiber is just as fast and less error-prone.
    
  968.   // Ideally we would have a special version of the work loop only
    
  969.   // for hydration.
    
  970.   popTreeContext(workInProgress);
    
  971.   switch (workInProgress.tag) {
    
  972.     case IndeterminateComponent:
    
  973.     case LazyComponent:
    
  974.     case SimpleMemoComponent:
    
  975.     case FunctionComponent:
    
  976.     case ForwardRef:
    
  977.     case Fragment:
    
  978.     case Mode:
    
  979.     case Profiler:
    
  980.     case ContextConsumer:
    
  981.     case MemoComponent:
    
  982.       bubbleProperties(workInProgress);
    
  983.       return null;
    
  984.     case ClassComponent: {
    
  985.       const Component = workInProgress.type;
    
  986.       if (isLegacyContextProvider(Component)) {
    
  987.         popLegacyContext(workInProgress);
    
  988.       }
    
  989.       bubbleProperties(workInProgress);
    
  990.       return null;
    
  991.     }
    
  992.     case HostRoot: {
    
  993.       const fiberRoot = (workInProgress.stateNode: FiberRoot);
    
  994. 
    
  995.       if (enableTransitionTracing) {
    
  996.         const transitions = getWorkInProgressTransitions();
    
  997.         // We set the Passive flag here because if there are new transitions,
    
  998.         // we will need to schedule callbacks and process the transitions,
    
  999.         // which we do in the passive phase
    
  1000.         if (transitions !== null) {
    
  1001.           workInProgress.flags |= Passive;
    
  1002.         }
    
  1003.       }
    
  1004. 
    
  1005.       if (enableCache) {
    
  1006.         let previousCache: Cache | null = null;
    
  1007.         if (current !== null) {
    
  1008.           previousCache = current.memoizedState.cache;
    
  1009.         }
    
  1010.         const cache: Cache = workInProgress.memoizedState.cache;
    
  1011.         if (cache !== previousCache) {
    
  1012.           // Run passive effects to retain/release the cache.
    
  1013.           workInProgress.flags |= Passive;
    
  1014.         }
    
  1015.         popCacheProvider(workInProgress, cache);
    
  1016.       }
    
  1017. 
    
  1018.       if (enableTransitionTracing) {
    
  1019.         popRootMarkerInstance(workInProgress);
    
  1020.       }
    
  1021. 
    
  1022.       popRootTransition(workInProgress, fiberRoot, renderLanes);
    
  1023.       popHostContainer(workInProgress);
    
  1024.       popTopLevelLegacyContextObject(workInProgress);
    
  1025.       if (fiberRoot.pendingContext) {
    
  1026.         fiberRoot.context = fiberRoot.pendingContext;
    
  1027.         fiberRoot.pendingContext = null;
    
  1028.       }
    
  1029.       if (current === null || current.child === null) {
    
  1030.         // If we hydrated, pop so that we can delete any remaining children
    
  1031.         // that weren't hydrated.
    
  1032.         const wasHydrated = popHydrationState(workInProgress);
    
  1033.         if (wasHydrated) {
    
  1034.           // If we hydrated, then we'll need to schedule an update for
    
  1035.           // the commit side-effects on the root.
    
  1036.           markUpdate(workInProgress);
    
  1037.         } else {
    
  1038.           if (current !== null) {
    
  1039.             const prevState: RootState = current.memoizedState;
    
  1040.             if (
    
  1041.               // Check if this is a client root
    
  1042.               !prevState.isDehydrated ||
    
  1043.               // Check if we reverted to client rendering (e.g. due to an error)
    
  1044.               (workInProgress.flags & ForceClientRender) !== NoFlags
    
  1045.             ) {
    
  1046.               // Schedule an effect to clear this container at the start of the
    
  1047.               // next commit. This handles the case of React rendering into a
    
  1048.               // container with previous children. It's also safe to do for
    
  1049.               // updates too, because current.child would only be null if the
    
  1050.               // previous render was null (so the container would already
    
  1051.               // be empty).
    
  1052.               workInProgress.flags |= Snapshot;
    
  1053. 
    
  1054.               // If this was a forced client render, there may have been
    
  1055.               // recoverable errors during first hydration attempt. If so, add
    
  1056.               // them to a queue so we can log them in the commit phase.
    
  1057.               upgradeHydrationErrorsToRecoverable();
    
  1058.             }
    
  1059.           }
    
  1060.         }
    
  1061.       }
    
  1062.       updateHostContainer(current, workInProgress);
    
  1063.       bubbleProperties(workInProgress);
    
  1064.       if (enableTransitionTracing) {
    
  1065.         if ((workInProgress.subtreeFlags & Visibility) !== NoFlags) {
    
  1066.           // If any of our suspense children toggle visibility, this means that
    
  1067.           // the pending boundaries array needs to be updated, which we only
    
  1068.           // do in the passive phase.
    
  1069.           workInProgress.flags |= Passive;
    
  1070.         }
    
  1071.       }
    
  1072.       return null;
    
  1073.     }
    
  1074.     case HostHoistable: {
    
  1075.       if (enableFloat && supportsResources) {
    
  1076.         // The branching here is more complicated than you might expect because
    
  1077.         // a HostHoistable sometimes corresponds to a Resource and sometimes
    
  1078.         // corresponds to an Instance. It can also switch during an update.
    
  1079. 
    
  1080.         const type = workInProgress.type;
    
  1081.         const nextResource: Resource | null = workInProgress.memoizedState;
    
  1082.         if (current === null) {
    
  1083.           // We are mounting and must Update this Hoistable in this commit
    
  1084.           // @TODO refactor this block to create the instance here in complete
    
  1085.           // phase if we are not hydrating.
    
  1086.           markUpdate(workInProgress);
    
  1087.           if (workInProgress.ref !== null) {
    
  1088.             markRef(workInProgress);
    
  1089.           }
    
  1090.           if (nextResource !== null) {
    
  1091.             // This is a Hoistable Resource
    
  1092. 
    
  1093.             // This must come at the very end of the complete phase.
    
  1094.             bubbleProperties(workInProgress);
    
  1095.             preloadResourceAndSuspendIfNeeded(
    
  1096.               workInProgress,
    
  1097.               nextResource,
    
  1098.               type,
    
  1099.               newProps,
    
  1100.               renderLanes,
    
  1101.             );
    
  1102.             return null;
    
  1103.           } else {
    
  1104.             // This is a Hoistable Instance
    
  1105. 
    
  1106.             // This must come at the very end of the complete phase.
    
  1107.             bubbleProperties(workInProgress);
    
  1108.             preloadInstanceAndSuspendIfNeeded(
    
  1109.               workInProgress,
    
  1110.               type,
    
  1111.               newProps,
    
  1112.               renderLanes,
    
  1113.             );
    
  1114.             return null;
    
  1115.           }
    
  1116.         } else {
    
  1117.           // We are updating.
    
  1118.           const currentResource = current.memoizedState;
    
  1119.           if (nextResource !== currentResource) {
    
  1120.             // We are transitioning to, from, or between Hoistable Resources
    
  1121.             // and require an update
    
  1122.             markUpdate(workInProgress);
    
  1123.           }
    
  1124.           if (current.ref !== workInProgress.ref) {
    
  1125.             markRef(workInProgress);
    
  1126.           }
    
  1127.           if (nextResource !== null) {
    
  1128.             // This is a Hoistable Resource
    
  1129.             // This must come at the very end of the complete phase.
    
  1130. 
    
  1131.             bubbleProperties(workInProgress);
    
  1132.             if (nextResource === currentResource) {
    
  1133.               workInProgress.flags &= ~MaySuspendCommit;
    
  1134.             } else {
    
  1135.               preloadResourceAndSuspendIfNeeded(
    
  1136.                 workInProgress,
    
  1137.                 nextResource,
    
  1138.                 type,
    
  1139.                 newProps,
    
  1140.                 renderLanes,
    
  1141.               );
    
  1142.             }
    
  1143.             return null;
    
  1144.           } else {
    
  1145.             // This is a Hoistable Instance
    
  1146.             // We may have props to update on the Hoistable instance.
    
  1147.             if (supportsMutation) {
    
  1148.               const oldProps = current.memoizedProps;
    
  1149.               if (oldProps !== newProps) {
    
  1150.                 markUpdate(workInProgress);
    
  1151.               }
    
  1152.             } else {
    
  1153.               // We use the updateHostComponent path becuase it produces
    
  1154.               // the update queue we need for Hoistables.
    
  1155.               updateHostComponent(
    
  1156.                 current,
    
  1157.                 workInProgress,
    
  1158.                 type,
    
  1159.                 newProps,
    
  1160.                 renderLanes,
    
  1161.               );
    
  1162.             }
    
  1163. 
    
  1164.             // This must come at the very end of the complete phase.
    
  1165.             bubbleProperties(workInProgress);
    
  1166.             preloadInstanceAndSuspendIfNeeded(
    
  1167.               workInProgress,
    
  1168.               type,
    
  1169.               newProps,
    
  1170.               renderLanes,
    
  1171.             );
    
  1172.             return null;
    
  1173.           }
    
  1174.         }
    
  1175.       }
    
  1176.       // Fall through
    
  1177.     }
    
  1178.     case HostSingleton: {
    
  1179.       if (enableHostSingletons && supportsSingletons) {
    
  1180.         popHostContext(workInProgress);
    
  1181.         const rootContainerInstance = getRootHostContainer();
    
  1182.         const type = workInProgress.type;
    
  1183.         if (current !== null && workInProgress.stateNode != null) {
    
  1184.           if (supportsMutation) {
    
  1185.             const oldProps = current.memoizedProps;
    
  1186.             if (oldProps !== newProps) {
    
  1187.               markUpdate(workInProgress);
    
  1188.             }
    
  1189.           } else {
    
  1190.             updateHostComponent(
    
  1191.               current,
    
  1192.               workInProgress,
    
  1193.               type,
    
  1194.               newProps,
    
  1195.               renderLanes,
    
  1196.             );
    
  1197.           }
    
  1198. 
    
  1199.           if (current.ref !== workInProgress.ref) {
    
  1200.             markRef(workInProgress);
    
  1201.           }
    
  1202.         } else {
    
  1203.           if (!newProps) {
    
  1204.             if (workInProgress.stateNode === null) {
    
  1205.               throw new Error(
    
  1206.                 'We must have new props for new mounts. This error is likely ' +
    
  1207.                   'caused by a bug in React. Please file an issue.',
    
  1208.               );
    
  1209.             }
    
  1210. 
    
  1211.             // This can happen when we abort work.
    
  1212.             bubbleProperties(workInProgress);
    
  1213.             return null;
    
  1214.           }
    
  1215. 
    
  1216.           const currentHostContext = getHostContext();
    
  1217.           const wasHydrated = popHydrationState(workInProgress);
    
  1218.           let instance: Instance;
    
  1219.           if (wasHydrated) {
    
  1220.             // We ignore the boolean indicating there is an updateQueue because
    
  1221.             // it is used only to set text children and HostSingletons do not
    
  1222.             // use them.
    
  1223.             prepareToHydrateHostInstance(workInProgress, currentHostContext);
    
  1224.             instance = workInProgress.stateNode;
    
  1225.           } else {
    
  1226.             instance = resolveSingletonInstance(
    
  1227.               type,
    
  1228.               newProps,
    
  1229.               rootContainerInstance,
    
  1230.               currentHostContext,
    
  1231.               true,
    
  1232.             );
    
  1233.             workInProgress.stateNode = instance;
    
  1234.             markUpdate(workInProgress);
    
  1235.           }
    
  1236. 
    
  1237.           if (workInProgress.ref !== null) {
    
  1238.             // If there is a ref on a host node we need to schedule a callback
    
  1239.             markRef(workInProgress);
    
  1240.           }
    
  1241.         }
    
  1242.         bubbleProperties(workInProgress);
    
  1243.         return null;
    
  1244.       }
    
  1245.       // Fall through
    
  1246.     }
    
  1247.     case HostComponent: {
    
  1248.       popHostContext(workInProgress);
    
  1249.       const type = workInProgress.type;
    
  1250.       if (current !== null && workInProgress.stateNode != null) {
    
  1251.         updateHostComponent(
    
  1252.           current,
    
  1253.           workInProgress,
    
  1254.           type,
    
  1255.           newProps,
    
  1256.           renderLanes,
    
  1257.         );
    
  1258. 
    
  1259.         if (current.ref !== workInProgress.ref) {
    
  1260.           markRef(workInProgress);
    
  1261.         }
    
  1262.       } else {
    
  1263.         if (!newProps) {
    
  1264.           if (workInProgress.stateNode === null) {
    
  1265.             throw new Error(
    
  1266.               'We must have new props for new mounts. This error is likely ' +
    
  1267.                 'caused by a bug in React. Please file an issue.',
    
  1268.             );
    
  1269.           }
    
  1270. 
    
  1271.           // This can happen when we abort work.
    
  1272.           bubbleProperties(workInProgress);
    
  1273.           return null;
    
  1274.         }
    
  1275. 
    
  1276.         const currentHostContext = getHostContext();
    
  1277.         // TODO: Move createInstance to beginWork and keep it on a context
    
  1278.         // "stack" as the parent. Then append children as we go in beginWork
    
  1279.         // or completeWork depending on whether we want to add them top->down or
    
  1280.         // bottom->up. Top->down is faster in IE11.
    
  1281.         const wasHydrated = popHydrationState(workInProgress);
    
  1282.         if (wasHydrated) {
    
  1283.           // TODO: Move this and createInstance step into the beginPhase
    
  1284.           // to consolidate.
    
  1285.           prepareToHydrateHostInstance(workInProgress, currentHostContext);
    
  1286.         } else {
    
  1287.           const rootContainerInstance = getRootHostContainer();
    
  1288.           const instance = createInstance(
    
  1289.             type,
    
  1290.             newProps,
    
  1291.             rootContainerInstance,
    
  1292.             currentHostContext,
    
  1293.             workInProgress,
    
  1294.           );
    
  1295.           // TODO: For persistent renderers, we should pass children as part
    
  1296.           // of the initial instance creation
    
  1297.           appendAllChildren(instance, workInProgress, false, false);
    
  1298.           workInProgress.stateNode = instance;
    
  1299. 
    
  1300.           // Certain renderers require commit-time effects for initial mount.
    
  1301.           // (eg DOM renderer supports auto-focus for certain elements).
    
  1302.           // Make sure such renderers get scheduled for later work.
    
  1303.           if (
    
  1304.             finalizeInitialChildren(
    
  1305.               instance,
    
  1306.               type,
    
  1307.               newProps,
    
  1308.               currentHostContext,
    
  1309.             )
    
  1310.           ) {
    
  1311.             markUpdate(workInProgress);
    
  1312.           }
    
  1313.         }
    
  1314. 
    
  1315.         if (workInProgress.ref !== null) {
    
  1316.           // If there is a ref on a host node we need to schedule a callback
    
  1317.           markRef(workInProgress);
    
  1318.         }
    
  1319.       }
    
  1320.       bubbleProperties(workInProgress);
    
  1321. 
    
  1322.       // This must come at the very end of the complete phase, because it might
    
  1323.       // throw to suspend, and if the resource immediately loads, the work loop
    
  1324.       // will resume rendering as if the work-in-progress completed. So it must
    
  1325.       // fully complete.
    
  1326.       preloadInstanceAndSuspendIfNeeded(
    
  1327.         workInProgress,
    
  1328.         workInProgress.type,
    
  1329.         workInProgress.pendingProps,
    
  1330.         renderLanes,
    
  1331.       );
    
  1332.       return null;
    
  1333.     }
    
  1334.     case HostText: {
    
  1335.       const newText = newProps;
    
  1336.       if (current && workInProgress.stateNode != null) {
    
  1337.         const oldText = current.memoizedProps;
    
  1338.         // If we have an alternate, that means this is an update and we need
    
  1339.         // to schedule a side-effect to do the updates.
    
  1340.         updateHostText(current, workInProgress, oldText, newText);
    
  1341.       } else {
    
  1342.         if (typeof newText !== 'string') {
    
  1343.           if (workInProgress.stateNode === null) {
    
  1344.             throw new Error(
    
  1345.               'We must have new props for new mounts. This error is likely ' +
    
  1346.                 'caused by a bug in React. Please file an issue.',
    
  1347.             );
    
  1348.           }
    
  1349.           // This can happen when we abort work.
    
  1350.         }
    
  1351.         const rootContainerInstance = getRootHostContainer();
    
  1352.         const currentHostContext = getHostContext();
    
  1353.         const wasHydrated = popHydrationState(workInProgress);
    
  1354.         if (wasHydrated) {
    
  1355.           if (prepareToHydrateHostTextInstance(workInProgress)) {
    
  1356.             markUpdate(workInProgress);
    
  1357.           }
    
  1358.         } else {
    
  1359.           workInProgress.stateNode = createTextInstance(
    
  1360.             newText,
    
  1361.             rootContainerInstance,
    
  1362.             currentHostContext,
    
  1363.             workInProgress,
    
  1364.           );
    
  1365.         }
    
  1366.       }
    
  1367.       bubbleProperties(workInProgress);
    
  1368.       return null;
    
  1369.     }
    
  1370.     case SuspenseComponent: {
    
  1371.       popSuspenseHandler(workInProgress);
    
  1372.       const nextState: null | SuspenseState = workInProgress.memoizedState;
    
  1373. 
    
  1374.       // Special path for dehydrated boundaries. We may eventually move this
    
  1375.       // to its own fiber type so that we can add other kinds of hydration
    
  1376.       // boundaries that aren't associated with a Suspense tree. In anticipation
    
  1377.       // of such a refactor, all the hydration logic is contained in
    
  1378.       // this branch.
    
  1379.       if (
    
  1380.         current === null ||
    
  1381.         (current.memoizedState !== null &&
    
  1382.           current.memoizedState.dehydrated !== null)
    
  1383.       ) {
    
  1384.         const fallthroughToNormalSuspensePath =
    
  1385.           completeDehydratedSuspenseBoundary(
    
  1386.             current,
    
  1387.             workInProgress,
    
  1388.             nextState,
    
  1389.           );
    
  1390.         if (!fallthroughToNormalSuspensePath) {
    
  1391.           if (workInProgress.flags & ForceClientRender) {
    
  1392.             // Special case. There were remaining unhydrated nodes. We treat
    
  1393.             // this as a mismatch. Revert to client rendering.
    
  1394.             return workInProgress;
    
  1395.           } else {
    
  1396.             // Did not finish hydrating, either because this is the initial
    
  1397.             // render or because something suspended.
    
  1398.             return null;
    
  1399.           }
    
  1400.         }
    
  1401. 
    
  1402.         // Continue with the normal Suspense path.
    
  1403.       }
    
  1404. 
    
  1405.       if ((workInProgress.flags & DidCapture) !== NoFlags) {
    
  1406.         // Something suspended. Re-render with the fallback children.
    
  1407.         workInProgress.lanes = renderLanes;
    
  1408.         // Do not reset the effect list.
    
  1409.         if (
    
  1410.           enableProfilerTimer &&
    
  1411.           (workInProgress.mode & ProfileMode) !== NoMode
    
  1412.         ) {
    
  1413.           transferActualDuration(workInProgress);
    
  1414.         }
    
  1415.         // Don't bubble properties in this case.
    
  1416.         return workInProgress;
    
  1417.       }
    
  1418. 
    
  1419.       const nextDidTimeout = nextState !== null;
    
  1420.       const prevDidTimeout =
    
  1421.         current !== null &&
    
  1422.         (current.memoizedState: null | SuspenseState) !== null;
    
  1423. 
    
  1424.       if (enableCache && nextDidTimeout) {
    
  1425.         const offscreenFiber: Fiber = (workInProgress.child: any);
    
  1426.         let previousCache: Cache | null = null;
    
  1427.         if (
    
  1428.           offscreenFiber.alternate !== null &&
    
  1429.           offscreenFiber.alternate.memoizedState !== null &&
    
  1430.           offscreenFiber.alternate.memoizedState.cachePool !== null
    
  1431.         ) {
    
  1432.           previousCache = offscreenFiber.alternate.memoizedState.cachePool.pool;
    
  1433.         }
    
  1434.         let cache: Cache | null = null;
    
  1435.         if (
    
  1436.           offscreenFiber.memoizedState !== null &&
    
  1437.           offscreenFiber.memoizedState.cachePool !== null
    
  1438.         ) {
    
  1439.           cache = offscreenFiber.memoizedState.cachePool.pool;
    
  1440.         }
    
  1441.         if (cache !== previousCache) {
    
  1442.           // Run passive effects to retain/release the cache.
    
  1443.           offscreenFiber.flags |= Passive;
    
  1444.         }
    
  1445.       }
    
  1446. 
    
  1447.       // If the suspended state of the boundary changes, we need to schedule
    
  1448.       // a passive effect, which is when we process the transitions
    
  1449.       if (nextDidTimeout !== prevDidTimeout) {
    
  1450.         if (enableTransitionTracing) {
    
  1451.           const offscreenFiber: Fiber = (workInProgress.child: any);
    
  1452.           offscreenFiber.flags |= Passive;
    
  1453.         }
    
  1454. 
    
  1455.         // If the suspended state of the boundary changes, we need to schedule
    
  1456.         // an effect to toggle the subtree's visibility. When we switch from
    
  1457.         // fallback -> primary, the inner Offscreen fiber schedules this effect
    
  1458.         // as part of its normal complete phase. But when we switch from
    
  1459.         // primary -> fallback, the inner Offscreen fiber does not have a complete
    
  1460.         // phase. So we need to schedule its effect here.
    
  1461.         //
    
  1462.         // We also use this flag to connect/disconnect the effects, but the same
    
  1463.         // logic applies: when re-connecting, the Offscreen fiber's complete
    
  1464.         // phase will handle scheduling the effect. It's only when the fallback
    
  1465.         // is active that we have to do anything special.
    
  1466.         if (nextDidTimeout) {
    
  1467.           const offscreenFiber: Fiber = (workInProgress.child: any);
    
  1468.           offscreenFiber.flags |= Visibility;
    
  1469.         }
    
  1470.       }
    
  1471. 
    
  1472.       const retryQueue: RetryQueue | null = (workInProgress.updateQueue: any);
    
  1473.       scheduleRetryEffect(workInProgress, retryQueue);
    
  1474. 
    
  1475.       if (
    
  1476.         enableSuspenseCallback &&
    
  1477.         workInProgress.updateQueue !== null &&
    
  1478.         workInProgress.memoizedProps.suspenseCallback != null
    
  1479.       ) {
    
  1480.         // Always notify the callback
    
  1481.         // TODO: Move to passive phase
    
  1482.         workInProgress.flags |= Update;
    
  1483.       }
    
  1484.       bubbleProperties(workInProgress);
    
  1485.       if (enableProfilerTimer) {
    
  1486.         if ((workInProgress.mode & ProfileMode) !== NoMode) {
    
  1487.           if (nextDidTimeout) {
    
  1488.             // Don't count time spent in a timed out Suspense subtree as part of the base duration.
    
  1489.             const primaryChildFragment = workInProgress.child;
    
  1490.             if (primaryChildFragment !== null) {
    
  1491.               // $FlowFixMe[unsafe-arithmetic] Flow doesn't support type casting in combination with the -= operator
    
  1492.               workInProgress.treeBaseDuration -=
    
  1493.                 ((primaryChildFragment.treeBaseDuration: any): number);
    
  1494.             }
    
  1495.           }
    
  1496.         }
    
  1497.       }
    
  1498.       return null;
    
  1499.     }
    
  1500.     case HostPortal:
    
  1501.       popHostContainer(workInProgress);
    
  1502.       updateHostContainer(current, workInProgress);
    
  1503.       if (current === null) {
    
  1504.         preparePortalMount(workInProgress.stateNode.containerInfo);
    
  1505.       }
    
  1506.       bubbleProperties(workInProgress);
    
  1507.       return null;
    
  1508.     case ContextProvider:
    
  1509.       // Pop provider fiber
    
  1510.       const context: ReactContext<any> = workInProgress.type._context;
    
  1511.       popProvider(context, workInProgress);
    
  1512.       bubbleProperties(workInProgress);
    
  1513.       return null;
    
  1514.     case IncompleteClassComponent: {
    
  1515.       // Same as class component case. I put it down here so that the tags are
    
  1516.       // sequential to ensure this switch is compiled to a jump table.
    
  1517.       const Component = workInProgress.type;
    
  1518.       if (isLegacyContextProvider(Component)) {
    
  1519.         popLegacyContext(workInProgress);
    
  1520.       }
    
  1521.       bubbleProperties(workInProgress);
    
  1522.       return null;
    
  1523.     }
    
  1524.     case SuspenseListComponent: {
    
  1525.       popSuspenseListContext(workInProgress);
    
  1526. 
    
  1527.       const renderState: null | SuspenseListRenderState =
    
  1528.         workInProgress.memoizedState;
    
  1529. 
    
  1530.       if (renderState === null) {
    
  1531.         // We're running in the default, "independent" mode.
    
  1532.         // We don't do anything in this mode.
    
  1533.         bubbleProperties(workInProgress);
    
  1534.         return null;
    
  1535.       }
    
  1536. 
    
  1537.       let didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags;
    
  1538. 
    
  1539.       const renderedTail = renderState.rendering;
    
  1540.       if (renderedTail === null) {
    
  1541.         // We just rendered the head.
    
  1542.         if (!didSuspendAlready) {
    
  1543.           // This is the first pass. We need to figure out if anything is still
    
  1544.           // suspended in the rendered set.
    
  1545. 
    
  1546.           // If new content unsuspended, but there's still some content that
    
  1547.           // didn't. Then we need to do a second pass that forces everything
    
  1548.           // to keep showing their fallbacks.
    
  1549. 
    
  1550.           // We might be suspended if something in this render pass suspended, or
    
  1551.           // something in the previous committed pass suspended. Otherwise,
    
  1552.           // there's no chance so we can skip the expensive call to
    
  1553.           // findFirstSuspended.
    
  1554.           const cannotBeSuspended =
    
  1555.             renderHasNotSuspendedYet() &&
    
  1556.             (current === null || (current.flags & DidCapture) === NoFlags);
    
  1557.           if (!cannotBeSuspended) {
    
  1558.             let row = workInProgress.child;
    
  1559.             while (row !== null) {
    
  1560.               const suspended = findFirstSuspended(row);
    
  1561.               if (suspended !== null) {
    
  1562.                 didSuspendAlready = true;
    
  1563.                 workInProgress.flags |= DidCapture;
    
  1564.                 cutOffTailIfNeeded(renderState, false);
    
  1565. 
    
  1566.                 // If this is a newly suspended tree, it might not get committed as
    
  1567.                 // part of the second pass. In that case nothing will subscribe to
    
  1568.                 // its thenables. Instead, we'll transfer its thenables to the
    
  1569.                 // SuspenseList so that it can retry if they resolve.
    
  1570.                 // There might be multiple of these in the list but since we're
    
  1571.                 // going to wait for all of them anyway, it doesn't really matter
    
  1572.                 // which ones gets to ping. In theory we could get clever and keep
    
  1573.                 // track of how many dependencies remain but it gets tricky because
    
  1574.                 // in the meantime, we can add/remove/change items and dependencies.
    
  1575.                 // We might bail out of the loop before finding any but that
    
  1576.                 // doesn't matter since that means that the other boundaries that
    
  1577.                 // we did find already has their listeners attached.
    
  1578.                 const retryQueue: RetryQueue | null =
    
  1579.                   (suspended.updateQueue: any);
    
  1580.                 workInProgress.updateQueue = retryQueue;
    
  1581.                 scheduleRetryEffect(workInProgress, retryQueue);
    
  1582. 
    
  1583.                 // Rerender the whole list, but this time, we'll force fallbacks
    
  1584.                 // to stay in place.
    
  1585.                 // Reset the effect flags before doing the second pass since that's now invalid.
    
  1586.                 // Reset the child fibers to their original state.
    
  1587.                 workInProgress.subtreeFlags = NoFlags;
    
  1588.                 resetChildFibers(workInProgress, renderLanes);
    
  1589. 
    
  1590.                 // Set up the Suspense List Context to force suspense and
    
  1591.                 // immediately rerender the children.
    
  1592.                 pushSuspenseListContext(
    
  1593.                   workInProgress,
    
  1594.                   setShallowSuspenseListContext(
    
  1595.                     suspenseStackCursor.current,
    
  1596.                     ForceSuspenseFallback,
    
  1597.                   ),
    
  1598.                 );
    
  1599.                 // Don't bubble properties in this case.
    
  1600.                 return workInProgress.child;
    
  1601.               }
    
  1602.               row = row.sibling;
    
  1603.             }
    
  1604.           }
    
  1605. 
    
  1606.           if (renderState.tail !== null && now() > getRenderTargetTime()) {
    
  1607.             // We have already passed our CPU deadline but we still have rows
    
  1608.             // left in the tail. We'll just give up further attempts to render
    
  1609.             // the main content and only render fallbacks.
    
  1610.             workInProgress.flags |= DidCapture;
    
  1611.             didSuspendAlready = true;
    
  1612. 
    
  1613.             cutOffTailIfNeeded(renderState, false);
    
  1614. 
    
  1615.             // Since nothing actually suspended, there will nothing to ping this
    
  1616.             // to get it started back up to attempt the next item. While in terms
    
  1617.             // of priority this work has the same priority as this current render,
    
  1618.             // it's not part of the same transition once the transition has
    
  1619.             // committed. If it's sync, we still want to yield so that it can be
    
  1620.             // painted. Conceptually, this is really the same as pinging.
    
  1621.             // We can use any RetryLane even if it's the one currently rendering
    
  1622.             // since we're leaving it behind on this node.
    
  1623.             workInProgress.lanes = SomeRetryLane;
    
  1624.           }
    
  1625.         } else {
    
  1626.           cutOffTailIfNeeded(renderState, false);
    
  1627.         }
    
  1628.         // Next we're going to render the tail.
    
  1629.       } else {
    
  1630.         // Append the rendered row to the child list.
    
  1631.         if (!didSuspendAlready) {
    
  1632.           const suspended = findFirstSuspended(renderedTail);
    
  1633.           if (suspended !== null) {
    
  1634.             workInProgress.flags |= DidCapture;
    
  1635.             didSuspendAlready = true;
    
  1636. 
    
  1637.             // Ensure we transfer the update queue to the parent so that it doesn't
    
  1638.             // get lost if this row ends up dropped during a second pass.
    
  1639.             const retryQueue: RetryQueue | null = (suspended.updateQueue: any);
    
  1640.             workInProgress.updateQueue = retryQueue;
    
  1641.             scheduleRetryEffect(workInProgress, retryQueue);
    
  1642. 
    
  1643.             cutOffTailIfNeeded(renderState, true);
    
  1644.             // This might have been modified.
    
  1645.             if (
    
  1646.               renderState.tail === null &&
    
  1647.               renderState.tailMode === 'hidden' &&
    
  1648.               !renderedTail.alternate &&
    
  1649.               !getIsHydrating() // We don't cut it if we're hydrating.
    
  1650.             ) {
    
  1651.               // We're done.
    
  1652.               bubbleProperties(workInProgress);
    
  1653.               return null;
    
  1654.             }
    
  1655.           } else if (
    
  1656.             // The time it took to render last row is greater than the remaining
    
  1657.             // time we have to render. So rendering one more row would likely
    
  1658.             // exceed it.
    
  1659.             now() * 2 - renderState.renderingStartTime >
    
  1660.               getRenderTargetTime() &&
    
  1661.             renderLanes !== OffscreenLane
    
  1662.           ) {
    
  1663.             // We have now passed our CPU deadline and we'll just give up further
    
  1664.             // attempts to render the main content and only render fallbacks.
    
  1665.             // The assumption is that this is usually faster.
    
  1666.             workInProgress.flags |= DidCapture;
    
  1667.             didSuspendAlready = true;
    
  1668. 
    
  1669.             cutOffTailIfNeeded(renderState, false);
    
  1670. 
    
  1671.             // Since nothing actually suspended, there will nothing to ping this
    
  1672.             // to get it started back up to attempt the next item. While in terms
    
  1673.             // of priority this work has the same priority as this current render,
    
  1674.             // it's not part of the same transition once the transition has
    
  1675.             // committed. If it's sync, we still want to yield so that it can be
    
  1676.             // painted. Conceptually, this is really the same as pinging.
    
  1677.             // We can use any RetryLane even if it's the one currently rendering
    
  1678.             // since we're leaving it behind on this node.
    
  1679.             workInProgress.lanes = SomeRetryLane;
    
  1680.           }
    
  1681.         }
    
  1682.         if (renderState.isBackwards) {
    
  1683.           // The effect list of the backwards tail will have been added
    
  1684.           // to the end. This breaks the guarantee that life-cycles fire in
    
  1685.           // sibling order but that isn't a strong guarantee promised by React.
    
  1686.           // Especially since these might also just pop in during future commits.
    
  1687.           // Append to the beginning of the list.
    
  1688.           renderedTail.sibling = workInProgress.child;
    
  1689.           workInProgress.child = renderedTail;
    
  1690.         } else {
    
  1691.           const previousSibling = renderState.last;
    
  1692.           if (previousSibling !== null) {
    
  1693.             previousSibling.sibling = renderedTail;
    
  1694.           } else {
    
  1695.             workInProgress.child = renderedTail;
    
  1696.           }
    
  1697.           renderState.last = renderedTail;
    
  1698.         }
    
  1699.       }
    
  1700. 
    
  1701.       if (renderState.tail !== null) {
    
  1702.         // We still have tail rows to render.
    
  1703.         // Pop a row.
    
  1704.         const next = renderState.tail;
    
  1705.         renderState.rendering = next;
    
  1706.         renderState.tail = next.sibling;
    
  1707.         renderState.renderingStartTime = now();
    
  1708.         next.sibling = null;
    
  1709. 
    
  1710.         // Restore the context.
    
  1711.         // TODO: We can probably just avoid popping it instead and only
    
  1712.         // setting it the first time we go from not suspended to suspended.
    
  1713.         let suspenseContext = suspenseStackCursor.current;
    
  1714.         if (didSuspendAlready) {
    
  1715.           suspenseContext = setShallowSuspenseListContext(
    
  1716.             suspenseContext,
    
  1717.             ForceSuspenseFallback,
    
  1718.           );
    
  1719.         } else {
    
  1720.           suspenseContext =
    
  1721.             setDefaultShallowSuspenseListContext(suspenseContext);
    
  1722.         }
    
  1723.         pushSuspenseListContext(workInProgress, suspenseContext);
    
  1724.         // Do a pass over the next row.
    
  1725.         // Don't bubble properties in this case.
    
  1726.         return next;
    
  1727.       }
    
  1728.       bubbleProperties(workInProgress);
    
  1729.       return null;
    
  1730.     }
    
  1731.     case ScopeComponent: {
    
  1732.       if (enableScopeAPI) {
    
  1733.         if (current === null) {
    
  1734.           const scopeInstance: ReactScopeInstance = createScopeInstance();
    
  1735.           workInProgress.stateNode = scopeInstance;
    
  1736.           prepareScopeUpdate(scopeInstance, workInProgress);
    
  1737.           if (workInProgress.ref !== null) {
    
  1738.             markRef(workInProgress);
    
  1739.             markUpdate(workInProgress);
    
  1740.           }
    
  1741.         } else {
    
  1742.           if (workInProgress.ref !== null) {
    
  1743.             markUpdate(workInProgress);
    
  1744.           }
    
  1745.           if (current.ref !== workInProgress.ref) {
    
  1746.             markRef(workInProgress);
    
  1747.           }
    
  1748.         }
    
  1749.         bubbleProperties(workInProgress);
    
  1750.         return null;
    
  1751.       }
    
  1752.       break;
    
  1753.     }
    
  1754.     case OffscreenComponent:
    
  1755.     case LegacyHiddenComponent: {
    
  1756.       popSuspenseHandler(workInProgress);
    
  1757.       popHiddenContext(workInProgress);
    
  1758.       const nextState: OffscreenState | null = workInProgress.memoizedState;
    
  1759.       const nextIsHidden = nextState !== null;
    
  1760. 
    
  1761.       // Schedule a Visibility effect if the visibility has changed
    
  1762.       if (enableLegacyHidden && workInProgress.tag === LegacyHiddenComponent) {
    
  1763.         // LegacyHidden doesn't do any hiding — it only pre-renders.
    
  1764.       } else {
    
  1765.         if (current !== null) {
    
  1766.           const prevState: OffscreenState | null = current.memoizedState;
    
  1767.           const prevIsHidden = prevState !== null;
    
  1768.           if (prevIsHidden !== nextIsHidden) {
    
  1769.             workInProgress.flags |= Visibility;
    
  1770.           }
    
  1771.         } else {
    
  1772.           // On initial mount, we only need a Visibility effect if the tree
    
  1773.           // is hidden.
    
  1774.           if (nextIsHidden) {
    
  1775.             workInProgress.flags |= Visibility;
    
  1776.           }
    
  1777.         }
    
  1778.       }
    
  1779. 
    
  1780.       if (!nextIsHidden || (workInProgress.mode & ConcurrentMode) === NoMode) {
    
  1781.         bubbleProperties(workInProgress);
    
  1782.       } else {
    
  1783.         // Don't bubble properties for hidden children unless we're rendering
    
  1784.         // at offscreen priority.
    
  1785.         if (
    
  1786.           includesSomeLane(renderLanes, (OffscreenLane: Lane)) &&
    
  1787.           // Also don't bubble if the tree suspended
    
  1788.           (workInProgress.flags & DidCapture) === NoLanes
    
  1789.         ) {
    
  1790.           bubbleProperties(workInProgress);
    
  1791.           // Check if there was an insertion or update in the hidden subtree.
    
  1792.           // If so, we need to hide those nodes in the commit phase, so
    
  1793.           // schedule a visibility effect.
    
  1794.           if (
    
  1795.             (!enableLegacyHidden ||
    
  1796.               workInProgress.tag !== LegacyHiddenComponent) &&
    
  1797.             workInProgress.subtreeFlags & (Placement | Update)
    
  1798.           ) {
    
  1799.             workInProgress.flags |= Visibility;
    
  1800.           }
    
  1801.         }
    
  1802.       }
    
  1803. 
    
  1804.       const offscreenQueue: OffscreenQueue | null =
    
  1805.         (workInProgress.updateQueue: any);
    
  1806.       if (offscreenQueue !== null) {
    
  1807.         const retryQueue = offscreenQueue.retryQueue;
    
  1808.         scheduleRetryEffect(workInProgress, retryQueue);
    
  1809.       }
    
  1810. 
    
  1811.       if (enableCache) {
    
  1812.         let previousCache: Cache | null = null;
    
  1813.         if (
    
  1814.           current !== null &&
    
  1815.           current.memoizedState !== null &&
    
  1816.           current.memoizedState.cachePool !== null
    
  1817.         ) {
    
  1818.           previousCache = current.memoizedState.cachePool.pool;
    
  1819.         }
    
  1820.         let cache: Cache | null = null;
    
  1821.         if (
    
  1822.           workInProgress.memoizedState !== null &&
    
  1823.           workInProgress.memoizedState.cachePool !== null
    
  1824.         ) {
    
  1825.           cache = workInProgress.memoizedState.cachePool.pool;
    
  1826.         }
    
  1827.         if (cache !== previousCache) {
    
  1828.           // Run passive effects to retain/release the cache.
    
  1829.           workInProgress.flags |= Passive;
    
  1830.         }
    
  1831.       }
    
  1832. 
    
  1833.       popTransition(workInProgress, current);
    
  1834. 
    
  1835.       return null;
    
  1836.     }
    
  1837.     case CacheComponent: {
    
  1838.       if (enableCache) {
    
  1839.         let previousCache: Cache | null = null;
    
  1840.         if (current !== null) {
    
  1841.           previousCache = current.memoizedState.cache;
    
  1842.         }
    
  1843.         const cache: Cache = workInProgress.memoizedState.cache;
    
  1844.         if (cache !== previousCache) {
    
  1845.           // Run passive effects to retain/release the cache.
    
  1846.           workInProgress.flags |= Passive;
    
  1847.         }
    
  1848.         popCacheProvider(workInProgress, cache);
    
  1849.         bubbleProperties(workInProgress);
    
  1850.       }
    
  1851.       return null;
    
  1852.     }
    
  1853.     case TracingMarkerComponent: {
    
  1854.       if (enableTransitionTracing) {
    
  1855.         const instance: TracingMarkerInstance | null = workInProgress.stateNode;
    
  1856.         if (instance !== null) {
    
  1857.           popMarkerInstance(workInProgress);
    
  1858.         }
    
  1859.         bubbleProperties(workInProgress);
    
  1860.       }
    
  1861.       return null;
    
  1862.     }
    
  1863.   }
    
  1864. 
    
  1865.   throw new Error(
    
  1866.     `Unknown unit of work tag (${workInProgress.tag}). This error is likely caused by a bug in ` +
    
  1867.       'React. Please file an issue.',
    
  1868.   );
    
  1869. }
    
  1870. 
    
  1871. export {completeWork};