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} from './ReactInternalTypes';
    
  11. import {NoMode, ConcurrentMode} from './ReactTypeOfMode';
    
  12. import type {
    
  13.   Instance,
    
  14.   TextInstance,
    
  15.   HydratableInstance,
    
  16.   SuspenseInstance,
    
  17.   Container,
    
  18.   HostContext,
    
  19. } from './ReactFiberConfig';
    
  20. import type {SuspenseState} from './ReactFiberSuspenseComponent';
    
  21. import type {TreeContext} from './ReactFiberTreeContext';
    
  22. import type {CapturedValue} from './ReactCapturedValue';
    
  23. 
    
  24. import {
    
  25.   HostComponent,
    
  26.   HostSingleton,
    
  27.   HostText,
    
  28.   HostRoot,
    
  29.   SuspenseComponent,
    
  30. } from './ReactWorkTags';
    
  31. import {
    
  32.   ChildDeletion,
    
  33.   Placement,
    
  34.   Hydrating,
    
  35.   NoFlags,
    
  36.   DidCapture,
    
  37. } from './ReactFiberFlags';
    
  38. import {
    
  39.   enableHostSingletons,
    
  40.   enableClientRenderFallbackOnTextMismatch,
    
  41. } from 'shared/ReactFeatureFlags';
    
  42. 
    
  43. import {
    
  44.   createFiberFromHostInstanceForDeletion,
    
  45.   createFiberFromDehydratedFragment,
    
  46. } from './ReactFiber';
    
  47. import {
    
  48.   shouldSetTextContent,
    
  49.   supportsHydration,
    
  50.   supportsSingletons,
    
  51.   getNextHydratableSibling,
    
  52.   getFirstHydratableChild,
    
  53.   getFirstHydratableChildWithinContainer,
    
  54.   getFirstHydratableChildWithinSuspenseInstance,
    
  55.   hydrateInstance,
    
  56.   hydrateTextInstance,
    
  57.   hydrateSuspenseInstance,
    
  58.   getNextHydratableInstanceAfterSuspenseInstance,
    
  59.   shouldDeleteUnhydratedTailInstances,
    
  60.   didNotMatchHydratedContainerTextInstance,
    
  61.   didNotMatchHydratedTextInstance,
    
  62.   didNotHydrateInstanceWithinContainer,
    
  63.   didNotHydrateInstanceWithinSuspenseInstance,
    
  64.   didNotHydrateInstance,
    
  65.   didNotFindHydratableInstanceWithinContainer,
    
  66.   didNotFindHydratableTextInstanceWithinContainer,
    
  67.   didNotFindHydratableSuspenseInstanceWithinContainer,
    
  68.   didNotFindHydratableInstanceWithinSuspenseInstance,
    
  69.   didNotFindHydratableTextInstanceWithinSuspenseInstance,
    
  70.   didNotFindHydratableSuspenseInstanceWithinSuspenseInstance,
    
  71.   didNotFindHydratableInstance,
    
  72.   didNotFindHydratableTextInstance,
    
  73.   didNotFindHydratableSuspenseInstance,
    
  74.   resolveSingletonInstance,
    
  75.   canHydrateInstance,
    
  76.   canHydrateTextInstance,
    
  77.   canHydrateSuspenseInstance,
    
  78.   canHydrateFormStateMarker,
    
  79.   isFormStateMarkerMatching,
    
  80.   isHydratableText,
    
  81. } from './ReactFiberConfig';
    
  82. import {OffscreenLane} from './ReactFiberLane';
    
  83. import {
    
  84.   getSuspendedTreeContext,
    
  85.   restoreSuspendedTreeContext,
    
  86. } from './ReactFiberTreeContext';
    
  87. import {queueRecoverableErrors} from './ReactFiberWorkLoop';
    
  88. import {getRootHostContainer, getHostContext} from './ReactFiberHostContext';
    
  89. 
    
  90. // The deepest Fiber on the stack involved in a hydration context.
    
  91. // This may have been an insertion or a hydration.
    
  92. let hydrationParentFiber: null | Fiber = null;
    
  93. let nextHydratableInstance: null | HydratableInstance = null;
    
  94. let isHydrating: boolean = false;
    
  95. 
    
  96. // This flag allows for warning supression when we expect there to be mismatches
    
  97. // due to earlier mismatches or a suspended fiber.
    
  98. let didSuspendOrErrorDEV: boolean = false;
    
  99. 
    
  100. // Hydration errors that were thrown inside this boundary
    
  101. let hydrationErrors: Array<CapturedValue<mixed>> | null = null;
    
  102. 
    
  103. let rootOrSingletonContext = false;
    
  104. 
    
  105. function warnIfHydrating() {
    
  106.   if (__DEV__) {
    
  107.     if (isHydrating) {
    
  108.       console.error(
    
  109.         'We should not be hydrating here. This is a bug in React. Please file a bug.',
    
  110.       );
    
  111.     }
    
  112.   }
    
  113. }
    
  114. 
    
  115. export function markDidThrowWhileHydratingDEV() {
    
  116.   if (__DEV__) {
    
  117.     didSuspendOrErrorDEV = true;
    
  118.   }
    
  119. }
    
  120. 
    
  121. export function didSuspendOrErrorWhileHydratingDEV(): boolean {
    
  122.   if (__DEV__) {
    
  123.     return didSuspendOrErrorDEV;
    
  124.   }
    
  125.   return false;
    
  126. }
    
  127. 
    
  128. function enterHydrationState(fiber: Fiber): boolean {
    
  129.   if (!supportsHydration) {
    
  130.     return false;
    
  131.   }
    
  132. 
    
  133.   const parentInstance: Container = fiber.stateNode.containerInfo;
    
  134.   nextHydratableInstance =
    
  135.     getFirstHydratableChildWithinContainer(parentInstance);
    
  136.   hydrationParentFiber = fiber;
    
  137.   isHydrating = true;
    
  138.   hydrationErrors = null;
    
  139.   didSuspendOrErrorDEV = false;
    
  140.   rootOrSingletonContext = true;
    
  141.   return true;
    
  142. }
    
  143. 
    
  144. function reenterHydrationStateFromDehydratedSuspenseInstance(
    
  145.   fiber: Fiber,
    
  146.   suspenseInstance: SuspenseInstance,
    
  147.   treeContext: TreeContext | null,
    
  148. ): boolean {
    
  149.   if (!supportsHydration) {
    
  150.     return false;
    
  151.   }
    
  152.   nextHydratableInstance =
    
  153.     getFirstHydratableChildWithinSuspenseInstance(suspenseInstance);
    
  154.   hydrationParentFiber = fiber;
    
  155.   isHydrating = true;
    
  156.   hydrationErrors = null;
    
  157.   didSuspendOrErrorDEV = false;
    
  158.   rootOrSingletonContext = false;
    
  159.   if (treeContext !== null) {
    
  160.     restoreSuspendedTreeContext(fiber, treeContext);
    
  161.   }
    
  162.   return true;
    
  163. }
    
  164. 
    
  165. function warnUnhydratedInstance(
    
  166.   returnFiber: Fiber,
    
  167.   instance: HydratableInstance,
    
  168. ) {
    
  169.   if (__DEV__) {
    
  170.     switch (returnFiber.tag) {
    
  171.       case HostRoot: {
    
  172.         didNotHydrateInstanceWithinContainer(
    
  173.           returnFiber.stateNode.containerInfo,
    
  174.           instance,
    
  175.         );
    
  176.         break;
    
  177.       }
    
  178.       case HostSingleton:
    
  179.       case HostComponent: {
    
  180.         const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
    
  181.         didNotHydrateInstance(
    
  182.           returnFiber.type,
    
  183.           returnFiber.memoizedProps,
    
  184.           returnFiber.stateNode,
    
  185.           instance,
    
  186.           // TODO: Delete this argument when we remove the legacy root API.
    
  187.           isConcurrentMode,
    
  188.         );
    
  189.         break;
    
  190.       }
    
  191.       case SuspenseComponent: {
    
  192.         const suspenseState: SuspenseState = returnFiber.memoizedState;
    
  193.         if (suspenseState.dehydrated !== null)
    
  194.           didNotHydrateInstanceWithinSuspenseInstance(
    
  195.             suspenseState.dehydrated,
    
  196.             instance,
    
  197.           );
    
  198.         break;
    
  199.       }
    
  200.     }
    
  201.   }
    
  202. }
    
  203. 
    
  204. function deleteHydratableInstance(
    
  205.   returnFiber: Fiber,
    
  206.   instance: HydratableInstance,
    
  207. ) {
    
  208.   warnUnhydratedInstance(returnFiber, instance);
    
  209.   const childToDelete = createFiberFromHostInstanceForDeletion();
    
  210.   childToDelete.stateNode = instance;
    
  211.   childToDelete.return = returnFiber;
    
  212. 
    
  213.   const deletions = returnFiber.deletions;
    
  214.   if (deletions === null) {
    
  215.     returnFiber.deletions = [childToDelete];
    
  216.     returnFiber.flags |= ChildDeletion;
    
  217.   } else {
    
  218.     deletions.push(childToDelete);
    
  219.   }
    
  220. }
    
  221. 
    
  222. function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) {
    
  223.   if (__DEV__) {
    
  224.     if (didSuspendOrErrorDEV) {
    
  225.       // Inside a boundary that already suspended. We're currently rendering the
    
  226.       // siblings of a suspended node. The mismatch may be due to the missing
    
  227.       // data, so it's probably a false positive.
    
  228.       return;
    
  229.     }
    
  230. 
    
  231.     switch (returnFiber.tag) {
    
  232.       case HostRoot: {
    
  233.         const parentContainer = returnFiber.stateNode.containerInfo;
    
  234.         switch (fiber.tag) {
    
  235.           case HostSingleton:
    
  236.           case HostComponent:
    
  237.             const type = fiber.type;
    
  238.             const props = fiber.pendingProps;
    
  239.             didNotFindHydratableInstanceWithinContainer(
    
  240.               parentContainer,
    
  241.               type,
    
  242.               props,
    
  243.             );
    
  244.             break;
    
  245.           case HostText:
    
  246.             const text = fiber.pendingProps;
    
  247.             didNotFindHydratableTextInstanceWithinContainer(
    
  248.               parentContainer,
    
  249.               text,
    
  250.             );
    
  251.             break;
    
  252.           case SuspenseComponent:
    
  253.             didNotFindHydratableSuspenseInstanceWithinContainer(
    
  254.               parentContainer,
    
  255.             );
    
  256.             break;
    
  257.         }
    
  258.         break;
    
  259.       }
    
  260.       case HostSingleton:
    
  261.       case HostComponent: {
    
  262.         const parentType = returnFiber.type;
    
  263.         const parentProps = returnFiber.memoizedProps;
    
  264.         const parentInstance = returnFiber.stateNode;
    
  265.         switch (fiber.tag) {
    
  266.           case HostSingleton:
    
  267.           case HostComponent: {
    
  268.             const type = fiber.type;
    
  269.             const props = fiber.pendingProps;
    
  270.             const isConcurrentMode =
    
  271.               (returnFiber.mode & ConcurrentMode) !== NoMode;
    
  272.             didNotFindHydratableInstance(
    
  273.               parentType,
    
  274.               parentProps,
    
  275.               parentInstance,
    
  276.               type,
    
  277.               props,
    
  278.               // TODO: Delete this argument when we remove the legacy root API.
    
  279.               isConcurrentMode,
    
  280.             );
    
  281.             break;
    
  282.           }
    
  283.           case HostText: {
    
  284.             const text = fiber.pendingProps;
    
  285.             const isConcurrentMode =
    
  286.               (returnFiber.mode & ConcurrentMode) !== NoMode;
    
  287.             didNotFindHydratableTextInstance(
    
  288.               parentType,
    
  289.               parentProps,
    
  290.               parentInstance,
    
  291.               text,
    
  292.               // TODO: Delete this argument when we remove the legacy root API.
    
  293.               isConcurrentMode,
    
  294.             );
    
  295.             break;
    
  296.           }
    
  297.           case SuspenseComponent: {
    
  298.             didNotFindHydratableSuspenseInstance(
    
  299.               parentType,
    
  300.               parentProps,
    
  301.               parentInstance,
    
  302.             );
    
  303.             break;
    
  304.           }
    
  305.         }
    
  306.         break;
    
  307.       }
    
  308.       case SuspenseComponent: {
    
  309.         const suspenseState: SuspenseState = returnFiber.memoizedState;
    
  310.         const parentInstance = suspenseState.dehydrated;
    
  311.         if (parentInstance !== null)
    
  312.           switch (fiber.tag) {
    
  313.             case HostSingleton:
    
  314.             case HostComponent:
    
  315.               const type = fiber.type;
    
  316.               const props = fiber.pendingProps;
    
  317.               didNotFindHydratableInstanceWithinSuspenseInstance(
    
  318.                 parentInstance,
    
  319.                 type,
    
  320.                 props,
    
  321.               );
    
  322.               break;
    
  323.             case HostText:
    
  324.               const text = fiber.pendingProps;
    
  325.               didNotFindHydratableTextInstanceWithinSuspenseInstance(
    
  326.                 parentInstance,
    
  327.                 text,
    
  328.               );
    
  329.               break;
    
  330.             case SuspenseComponent:
    
  331.               didNotFindHydratableSuspenseInstanceWithinSuspenseInstance(
    
  332.                 parentInstance,
    
  333.               );
    
  334.               break;
    
  335.           }
    
  336.         break;
    
  337.       }
    
  338.       default:
    
  339.         return;
    
  340.     }
    
  341.   }
    
  342. }
    
  343. function insertNonHydratedInstance(returnFiber: Fiber, fiber: Fiber) {
    
  344.   fiber.flags = (fiber.flags & ~Hydrating) | Placement;
    
  345.   warnNonhydratedInstance(returnFiber, fiber);
    
  346. }
    
  347. 
    
  348. function tryHydrateInstance(fiber: Fiber, nextInstance: any) {
    
  349.   // fiber is a HostComponent Fiber
    
  350.   const instance = canHydrateInstance(
    
  351.     nextInstance,
    
  352.     fiber.type,
    
  353.     fiber.pendingProps,
    
  354.     rootOrSingletonContext,
    
  355.   );
    
  356.   if (instance !== null) {
    
  357.     fiber.stateNode = (instance: Instance);
    
  358.     hydrationParentFiber = fiber;
    
  359.     nextHydratableInstance = getFirstHydratableChild(instance);
    
  360.     rootOrSingletonContext = false;
    
  361.     return true;
    
  362.   }
    
  363.   return false;
    
  364. }
    
  365. 
    
  366. function tryHydrateText(fiber: Fiber, nextInstance: any) {
    
  367.   // fiber is a HostText Fiber
    
  368.   const text = fiber.pendingProps;
    
  369.   const textInstance = canHydrateTextInstance(
    
  370.     nextInstance,
    
  371.     text,
    
  372.     rootOrSingletonContext,
    
  373.   );
    
  374.   if (textInstance !== null) {
    
  375.     fiber.stateNode = (textInstance: TextInstance);
    
  376.     hydrationParentFiber = fiber;
    
  377.     // Text Instances don't have children so there's nothing to hydrate.
    
  378.     nextHydratableInstance = null;
    
  379.     return true;
    
  380.   }
    
  381.   return false;
    
  382. }
    
  383. 
    
  384. function tryHydrateSuspense(fiber: Fiber, nextInstance: any) {
    
  385.   // fiber is a SuspenseComponent Fiber
    
  386.   const suspenseInstance = canHydrateSuspenseInstance(
    
  387.     nextInstance,
    
  388.     rootOrSingletonContext,
    
  389.   );
    
  390.   if (suspenseInstance !== null) {
    
  391.     const suspenseState: SuspenseState = {
    
  392.       dehydrated: suspenseInstance,
    
  393.       treeContext: getSuspendedTreeContext(),
    
  394.       retryLane: OffscreenLane,
    
  395.     };
    
  396.     fiber.memoizedState = suspenseState;
    
  397.     // Store the dehydrated fragment as a child fiber.
    
  398.     // This simplifies the code for getHostSibling and deleting nodes,
    
  399.     // since it doesn't have to consider all Suspense boundaries and
    
  400.     // check if they're dehydrated ones or not.
    
  401.     const dehydratedFragment =
    
  402.       createFiberFromDehydratedFragment(suspenseInstance);
    
  403.     dehydratedFragment.return = fiber;
    
  404.     fiber.child = dehydratedFragment;
    
  405.     hydrationParentFiber = fiber;
    
  406.     // While a Suspense Instance does have children, we won't step into
    
  407.     // it during the first pass. Instead, we'll reenter it later.
    
  408.     nextHydratableInstance = null;
    
  409.     return true;
    
  410.   }
    
  411.   return false;
    
  412. }
    
  413. 
    
  414. function shouldClientRenderOnMismatch(fiber: Fiber) {
    
  415.   return (
    
  416.     (fiber.mode & ConcurrentMode) !== NoMode &&
    
  417.     (fiber.flags & DidCapture) === NoFlags
    
  418.   );
    
  419. }
    
  420. 
    
  421. function throwOnHydrationMismatch(fiber: Fiber) {
    
  422.   throw new Error(
    
  423.     'Hydration failed because the initial UI does not match what was ' +
    
  424.       'rendered on the server.',
    
  425.   );
    
  426. }
    
  427. 
    
  428. function claimHydratableSingleton(fiber: Fiber): void {
    
  429.   if (enableHostSingletons && supportsSingletons) {
    
  430.     if (!isHydrating) {
    
  431.       return;
    
  432.     }
    
  433.     const currentRootContainer = getRootHostContainer();
    
  434.     const currentHostContext = getHostContext();
    
  435.     const instance = (fiber.stateNode = resolveSingletonInstance(
    
  436.       fiber.type,
    
  437.       fiber.pendingProps,
    
  438.       currentRootContainer,
    
  439.       currentHostContext,
    
  440.       false,
    
  441.     ));
    
  442.     hydrationParentFiber = fiber;
    
  443.     rootOrSingletonContext = true;
    
  444.     nextHydratableInstance = getFirstHydratableChild(instance);
    
  445.   }
    
  446. }
    
  447. 
    
  448. function tryToClaimNextHydratableInstance(fiber: Fiber): void {
    
  449.   if (!isHydrating) {
    
  450.     return;
    
  451.   }
    
  452.   const initialInstance = nextHydratableInstance;
    
  453.   const nextInstance = nextHydratableInstance;
    
  454.   if (!nextInstance) {
    
  455.     if (shouldClientRenderOnMismatch(fiber)) {
    
  456.       warnNonhydratedInstance((hydrationParentFiber: any), fiber);
    
  457.       throwOnHydrationMismatch(fiber);
    
  458.     }
    
  459.     // Nothing to hydrate. Make it an insertion.
    
  460.     insertNonHydratedInstance((hydrationParentFiber: any), fiber);
    
  461.     isHydrating = false;
    
  462.     hydrationParentFiber = fiber;
    
  463.     nextHydratableInstance = initialInstance;
    
  464.     return;
    
  465.   }
    
  466.   const firstAttemptedInstance = nextInstance;
    
  467.   if (!tryHydrateInstance(fiber, nextInstance)) {
    
  468.     if (shouldClientRenderOnMismatch(fiber)) {
    
  469.       warnNonhydratedInstance((hydrationParentFiber: any), fiber);
    
  470.       throwOnHydrationMismatch(fiber);
    
  471.     }
    
  472.     // If we can't hydrate this instance let's try the next one.
    
  473.     // We use this as a heuristic. It's based on intuition and not data so it
    
  474.     // might be flawed or unnecessary.
    
  475.     nextHydratableInstance = getNextHydratableSibling(nextInstance);
    
  476.     const prevHydrationParentFiber: Fiber = (hydrationParentFiber: any);
    
  477.     if (
    
  478.       !nextHydratableInstance ||
    
  479.       !tryHydrateInstance(fiber, nextHydratableInstance)
    
  480.     ) {
    
  481.       // Nothing to hydrate. Make it an insertion.
    
  482.       insertNonHydratedInstance((hydrationParentFiber: any), fiber);
    
  483.       isHydrating = false;
    
  484.       hydrationParentFiber = fiber;
    
  485.       nextHydratableInstance = initialInstance;
    
  486.       return;
    
  487.     }
    
  488.     // We matched the next one, we'll now assume that the first one was
    
  489.     // superfluous and we'll delete it. Since we can't eagerly delete it
    
  490.     // we'll have to schedule a deletion. To do that, this node needs a dummy
    
  491.     // fiber associated with it.
    
  492.     deleteHydratableInstance(prevHydrationParentFiber, firstAttemptedInstance);
    
  493.   }
    
  494. }
    
  495. 
    
  496. function tryToClaimNextHydratableTextInstance(fiber: Fiber): void {
    
  497.   if (!isHydrating) {
    
  498.     return;
    
  499.   }
    
  500.   const text = fiber.pendingProps;
    
  501.   const isHydratable = isHydratableText(text);
    
  502. 
    
  503.   const initialInstance = nextHydratableInstance;
    
  504.   const nextInstance = nextHydratableInstance;
    
  505.   if (!nextInstance || !isHydratable) {
    
  506.     // We exclude non hydrabable text because we know there are no matching hydratables.
    
  507.     // We either throw or insert depending on the render mode.
    
  508.     if (shouldClientRenderOnMismatch(fiber)) {
    
  509.       warnNonhydratedInstance((hydrationParentFiber: any), fiber);
    
  510.       throwOnHydrationMismatch(fiber);
    
  511.     }
    
  512.     // Nothing to hydrate. Make it an insertion.
    
  513.     insertNonHydratedInstance((hydrationParentFiber: any), fiber);
    
  514.     isHydrating = false;
    
  515.     hydrationParentFiber = fiber;
    
  516.     nextHydratableInstance = initialInstance;
    
  517.     return;
    
  518.   }
    
  519.   const firstAttemptedInstance = nextInstance;
    
  520.   if (!tryHydrateText(fiber, nextInstance)) {
    
  521.     if (shouldClientRenderOnMismatch(fiber)) {
    
  522.       warnNonhydratedInstance((hydrationParentFiber: any), fiber);
    
  523.       throwOnHydrationMismatch(fiber);
    
  524.     }
    
  525.     // If we can't hydrate this instance let's try the next one.
    
  526.     // We use this as a heuristic. It's based on intuition and not data so it
    
  527.     // might be flawed or unnecessary.
    
  528.     nextHydratableInstance = getNextHydratableSibling(nextInstance);
    
  529.     const prevHydrationParentFiber: Fiber = (hydrationParentFiber: any);
    
  530. 
    
  531.     if (
    
  532.       !nextHydratableInstance ||
    
  533.       !tryHydrateText(fiber, nextHydratableInstance)
    
  534.     ) {
    
  535.       // Nothing to hydrate. Make it an insertion.
    
  536.       insertNonHydratedInstance((hydrationParentFiber: any), fiber);
    
  537.       isHydrating = false;
    
  538.       hydrationParentFiber = fiber;
    
  539.       nextHydratableInstance = initialInstance;
    
  540.       return;
    
  541.     }
    
  542.     // We matched the next one, we'll now assume that the first one was
    
  543.     // superfluous and we'll delete it. Since we can't eagerly delete it
    
  544.     // we'll have to schedule a deletion. To do that, this node needs a dummy
    
  545.     // fiber associated with it.
    
  546.     deleteHydratableInstance(prevHydrationParentFiber, firstAttemptedInstance);
    
  547.   }
    
  548. }
    
  549. 
    
  550. function tryToClaimNextHydratableSuspenseInstance(fiber: Fiber): void {
    
  551.   if (!isHydrating) {
    
  552.     return;
    
  553.   }
    
  554.   const initialInstance = nextHydratableInstance;
    
  555.   const nextInstance = nextHydratableInstance;
    
  556.   if (!nextInstance) {
    
  557.     if (shouldClientRenderOnMismatch(fiber)) {
    
  558.       warnNonhydratedInstance((hydrationParentFiber: any), fiber);
    
  559.       throwOnHydrationMismatch(fiber);
    
  560.     }
    
  561.     // Nothing to hydrate. Make it an insertion.
    
  562.     insertNonHydratedInstance((hydrationParentFiber: any), fiber);
    
  563.     isHydrating = false;
    
  564.     hydrationParentFiber = fiber;
    
  565.     nextHydratableInstance = initialInstance;
    
  566.     return;
    
  567.   }
    
  568.   const firstAttemptedInstance = nextInstance;
    
  569.   if (!tryHydrateSuspense(fiber, nextInstance)) {
    
  570.     if (shouldClientRenderOnMismatch(fiber)) {
    
  571.       warnNonhydratedInstance((hydrationParentFiber: any), fiber);
    
  572.       throwOnHydrationMismatch(fiber);
    
  573.     }
    
  574.     // If we can't hydrate this instance let's try the next one.
    
  575.     // We use this as a heuristic. It's based on intuition and not data so it
    
  576.     // might be flawed or unnecessary.
    
  577.     nextHydratableInstance = getNextHydratableSibling(nextInstance);
    
  578.     const prevHydrationParentFiber: Fiber = (hydrationParentFiber: any);
    
  579. 
    
  580.     if (
    
  581.       !nextHydratableInstance ||
    
  582.       !tryHydrateSuspense(fiber, nextHydratableInstance)
    
  583.     ) {
    
  584.       // Nothing to hydrate. Make it an insertion.
    
  585.       insertNonHydratedInstance((hydrationParentFiber: any), fiber);
    
  586.       isHydrating = false;
    
  587.       hydrationParentFiber = fiber;
    
  588.       nextHydratableInstance = initialInstance;
    
  589.       return;
    
  590.     }
    
  591.     // We matched the next one, we'll now assume that the first one was
    
  592.     // superfluous and we'll delete it. Since we can't eagerly delete it
    
  593.     // we'll have to schedule a deletion. To do that, this node needs a dummy
    
  594.     // fiber associated with it.
    
  595.     deleteHydratableInstance(prevHydrationParentFiber, firstAttemptedInstance);
    
  596.   }
    
  597. }
    
  598. 
    
  599. export function tryToClaimNextHydratableFormMarkerInstance(
    
  600.   fiber: Fiber,
    
  601. ): boolean {
    
  602.   if (!isHydrating) {
    
  603.     return false;
    
  604.   }
    
  605.   if (nextHydratableInstance) {
    
  606.     const markerInstance = canHydrateFormStateMarker(
    
  607.       nextHydratableInstance,
    
  608.       rootOrSingletonContext,
    
  609.     );
    
  610.     if (markerInstance) {
    
  611.       // Found the marker instance.
    
  612.       nextHydratableInstance = getNextHydratableSibling(markerInstance);
    
  613.       // Return true if this marker instance should use the state passed
    
  614.       // to hydrateRoot.
    
  615.       // TODO: As an optimization, Fizz should only emit these markers if form
    
  616.       // state is passed at the root.
    
  617.       return isFormStateMarkerMatching(markerInstance);
    
  618.     }
    
  619.   }
    
  620.   // Should have found a marker instance. Throw an error to trigger client
    
  621.   // rendering. We don't bother to check if we're in a concurrent root because
    
  622.   // useFormState is a new API, so backwards compat is not an issue.
    
  623.   throwOnHydrationMismatch(fiber);
    
  624.   return false;
    
  625. }
    
  626. 
    
  627. function prepareToHydrateHostInstance(
    
  628.   fiber: Fiber,
    
  629.   hostContext: HostContext,
    
  630. ): void {
    
  631.   if (!supportsHydration) {
    
  632.     throw new Error(
    
  633.       'Expected prepareToHydrateHostInstance() to never be called. ' +
    
  634.         'This error is likely caused by a bug in React. Please file an issue.',
    
  635.     );
    
  636.   }
    
  637. 
    
  638.   const instance: Instance = fiber.stateNode;
    
  639.   const shouldWarnIfMismatchDev = !didSuspendOrErrorDEV;
    
  640.   hydrateInstance(
    
  641.     instance,
    
  642.     fiber.type,
    
  643.     fiber.memoizedProps,
    
  644.     hostContext,
    
  645.     fiber,
    
  646.     shouldWarnIfMismatchDev,
    
  647.   );
    
  648. }
    
  649. 
    
  650. function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
    
  651.   if (!supportsHydration) {
    
  652.     throw new Error(
    
  653.       'Expected prepareToHydrateHostTextInstance() to never be called. ' +
    
  654.         'This error is likely caused by a bug in React. Please file an issue.',
    
  655.     );
    
  656.   }
    
  657. 
    
  658.   const textInstance: TextInstance = fiber.stateNode;
    
  659.   const textContent: string = fiber.memoizedProps;
    
  660.   const shouldWarnIfMismatchDev = !didSuspendOrErrorDEV;
    
  661.   const shouldUpdate = hydrateTextInstance(
    
  662.     textInstance,
    
  663.     textContent,
    
  664.     fiber,
    
  665.     shouldWarnIfMismatchDev,
    
  666.   );
    
  667.   if (shouldUpdate) {
    
  668.     // We assume that prepareToHydrateHostTextInstance is called in a context where the
    
  669.     // hydration parent is the parent host component of this host text.
    
  670.     const returnFiber = hydrationParentFiber;
    
  671.     if (returnFiber !== null) {
    
  672.       switch (returnFiber.tag) {
    
  673.         case HostRoot: {
    
  674.           const parentContainer = returnFiber.stateNode.containerInfo;
    
  675.           const isConcurrentMode =
    
  676.             (returnFiber.mode & ConcurrentMode) !== NoMode;
    
  677.           didNotMatchHydratedContainerTextInstance(
    
  678.             parentContainer,
    
  679.             textInstance,
    
  680.             textContent,
    
  681.             // TODO: Delete this argument when we remove the legacy root API.
    
  682.             isConcurrentMode,
    
  683.             shouldWarnIfMismatchDev,
    
  684.           );
    
  685.           if (isConcurrentMode && enableClientRenderFallbackOnTextMismatch) {
    
  686.             // In concurrent mode we never update the mismatched text,
    
  687.             // even if the error was ignored.
    
  688.             return false;
    
  689.           }
    
  690.           break;
    
  691.         }
    
  692.         case HostSingleton:
    
  693.         case HostComponent: {
    
  694.           const parentType = returnFiber.type;
    
  695.           const parentProps = returnFiber.memoizedProps;
    
  696.           const parentInstance = returnFiber.stateNode;
    
  697.           const isConcurrentMode =
    
  698.             (returnFiber.mode & ConcurrentMode) !== NoMode;
    
  699.           didNotMatchHydratedTextInstance(
    
  700.             parentType,
    
  701.             parentProps,
    
  702.             parentInstance,
    
  703.             textInstance,
    
  704.             textContent,
    
  705.             // TODO: Delete this argument when we remove the legacy root API.
    
  706.             isConcurrentMode,
    
  707.             shouldWarnIfMismatchDev,
    
  708.           );
    
  709.           if (isConcurrentMode && enableClientRenderFallbackOnTextMismatch) {
    
  710.             // In concurrent mode we never update the mismatched text,
    
  711.             // even if the error was ignored.
    
  712.             return false;
    
  713.           }
    
  714.           break;
    
  715.         }
    
  716.       }
    
  717.     }
    
  718.   }
    
  719.   return shouldUpdate;
    
  720. }
    
  721. 
    
  722. function prepareToHydrateHostSuspenseInstance(fiber: Fiber): void {
    
  723.   if (!supportsHydration) {
    
  724.     throw new Error(
    
  725.       'Expected prepareToHydrateHostSuspenseInstance() to never be called. ' +
    
  726.         'This error is likely caused by a bug in React. Please file an issue.',
    
  727.     );
    
  728.   }
    
  729. 
    
  730.   const suspenseState: null | SuspenseState = fiber.memoizedState;
    
  731.   const suspenseInstance: null | SuspenseInstance =
    
  732.     suspenseState !== null ? suspenseState.dehydrated : null;
    
  733. 
    
  734.   if (!suspenseInstance) {
    
  735.     throw new Error(
    
  736.       'Expected to have a hydrated suspense instance. ' +
    
  737.         'This error is likely caused by a bug in React. Please file an issue.',
    
  738.     );
    
  739.   }
    
  740. 
    
  741.   hydrateSuspenseInstance(suspenseInstance, fiber);
    
  742. }
    
  743. 
    
  744. function skipPastDehydratedSuspenseInstance(
    
  745.   fiber: Fiber,
    
  746. ): null | HydratableInstance {
    
  747.   if (!supportsHydration) {
    
  748.     throw new Error(
    
  749.       'Expected skipPastDehydratedSuspenseInstance() to never be called. ' +
    
  750.         'This error is likely caused by a bug in React. Please file an issue.',
    
  751.     );
    
  752.   }
    
  753.   const suspenseState: null | SuspenseState = fiber.memoizedState;
    
  754.   const suspenseInstance: null | SuspenseInstance =
    
  755.     suspenseState !== null ? suspenseState.dehydrated : null;
    
  756. 
    
  757.   if (!suspenseInstance) {
    
  758.     throw new Error(
    
  759.       'Expected to have a hydrated suspense instance. ' +
    
  760.         'This error is likely caused by a bug in React. Please file an issue.',
    
  761.     );
    
  762.   }
    
  763. 
    
  764.   return getNextHydratableInstanceAfterSuspenseInstance(suspenseInstance);
    
  765. }
    
  766. 
    
  767. function popToNextHostParent(fiber: Fiber): void {
    
  768.   hydrationParentFiber = fiber.return;
    
  769.   while (hydrationParentFiber) {
    
  770.     switch (hydrationParentFiber.tag) {
    
  771.       case HostRoot:
    
  772.       case HostSingleton:
    
  773.         rootOrSingletonContext = true;
    
  774.         return;
    
  775.       case HostComponent:
    
  776.       case SuspenseComponent:
    
  777.         rootOrSingletonContext = false;
    
  778.         return;
    
  779.       default:
    
  780.         hydrationParentFiber = hydrationParentFiber.return;
    
  781.     }
    
  782.   }
    
  783. }
    
  784. 
    
  785. function popHydrationState(fiber: Fiber): boolean {
    
  786.   if (!supportsHydration) {
    
  787.     return false;
    
  788.   }
    
  789.   if (fiber !== hydrationParentFiber) {
    
  790.     // We're deeper than the current hydration context, inside an inserted
    
  791.     // tree.
    
  792.     return false;
    
  793.   }
    
  794.   if (!isHydrating) {
    
  795.     // If we're not currently hydrating but we're in a hydration context, then
    
  796.     // we were an insertion and now need to pop up reenter hydration of our
    
  797.     // siblings.
    
  798.     popToNextHostParent(fiber);
    
  799.     isHydrating = true;
    
  800.     return false;
    
  801.   }
    
  802. 
    
  803.   let shouldClear = false;
    
  804.   if (enableHostSingletons && supportsSingletons) {
    
  805.     // With float we never clear the Root, or Singleton instances. We also do not clear Instances
    
  806.     // that have singleton text content
    
  807.     if (
    
  808.       fiber.tag !== HostRoot &&
    
  809.       fiber.tag !== HostSingleton &&
    
  810.       !(
    
  811.         fiber.tag === HostComponent &&
    
  812.         (!shouldDeleteUnhydratedTailInstances(fiber.type) ||
    
  813.           shouldSetTextContent(fiber.type, fiber.memoizedProps))
    
  814.       )
    
  815.     ) {
    
  816.       shouldClear = true;
    
  817.     }
    
  818.   } else {
    
  819.     // If we have any remaining hydratable nodes, we need to delete them now.
    
  820.     // We only do this deeper than head and body since they tend to have random
    
  821.     // other nodes in them. We also ignore components with pure text content in
    
  822.     // side of them. We also don't delete anything inside the root container.
    
  823.     if (
    
  824.       fiber.tag !== HostRoot &&
    
  825.       (fiber.tag !== HostComponent ||
    
  826.         (shouldDeleteUnhydratedTailInstances(fiber.type) &&
    
  827.           !shouldSetTextContent(fiber.type, fiber.memoizedProps)))
    
  828.     ) {
    
  829.       shouldClear = true;
    
  830.     }
    
  831.   }
    
  832.   if (shouldClear) {
    
  833.     let nextInstance = nextHydratableInstance;
    
  834.     if (nextInstance) {
    
  835.       if (shouldClientRenderOnMismatch(fiber)) {
    
  836.         warnIfUnhydratedTailNodes(fiber);
    
  837.         throwOnHydrationMismatch(fiber);
    
  838.       } else {
    
  839.         while (nextInstance) {
    
  840.           deleteHydratableInstance(fiber, nextInstance);
    
  841.           nextInstance = getNextHydratableSibling(nextInstance);
    
  842.         }
    
  843.       }
    
  844.     }
    
  845.   }
    
  846.   popToNextHostParent(fiber);
    
  847.   if (fiber.tag === SuspenseComponent) {
    
  848.     nextHydratableInstance = skipPastDehydratedSuspenseInstance(fiber);
    
  849.   } else {
    
  850.     nextHydratableInstance = hydrationParentFiber
    
  851.       ? getNextHydratableSibling(fiber.stateNode)
    
  852.       : null;
    
  853.   }
    
  854.   return true;
    
  855. }
    
  856. 
    
  857. function hasUnhydratedTailNodes(): boolean {
    
  858.   return isHydrating && nextHydratableInstance !== null;
    
  859. }
    
  860. 
    
  861. function warnIfUnhydratedTailNodes(fiber: Fiber) {
    
  862.   let nextInstance = nextHydratableInstance;
    
  863.   while (nextInstance) {
    
  864.     warnUnhydratedInstance(fiber, nextInstance);
    
  865.     nextInstance = getNextHydratableSibling(nextInstance);
    
  866.   }
    
  867. }
    
  868. 
    
  869. function resetHydrationState(): void {
    
  870.   if (!supportsHydration) {
    
  871.     return;
    
  872.   }
    
  873. 
    
  874.   hydrationParentFiber = null;
    
  875.   nextHydratableInstance = null;
    
  876.   isHydrating = false;
    
  877.   didSuspendOrErrorDEV = false;
    
  878. }
    
  879. 
    
  880. export function upgradeHydrationErrorsToRecoverable(): void {
    
  881.   if (hydrationErrors !== null) {
    
  882.     // Successfully completed a forced client render. The errors that occurred
    
  883.     // during the hydration attempt are now recovered. We will log them in
    
  884.     // commit phase, once the entire tree has finished.
    
  885.     queueRecoverableErrors(hydrationErrors);
    
  886.     hydrationErrors = null;
    
  887.   }
    
  888. }
    
  889. 
    
  890. function getIsHydrating(): boolean {
    
  891.   return isHydrating;
    
  892. }
    
  893. 
    
  894. export function queueHydrationError(error: CapturedValue<mixed>): void {
    
  895.   if (hydrationErrors === null) {
    
  896.     hydrationErrors = [error];
    
  897.   } else {
    
  898.     hydrationErrors.push(error);
    
  899.   }
    
  900. }
    
  901. 
    
  902. export {
    
  903.   warnIfHydrating,
    
  904.   enterHydrationState,
    
  905.   getIsHydrating,
    
  906.   reenterHydrationStateFromDehydratedSuspenseInstance,
    
  907.   resetHydrationState,
    
  908.   claimHydratableSingleton,
    
  909.   tryToClaimNextHydratableInstance,
    
  910.   tryToClaimNextHydratableTextInstance,
    
  911.   tryToClaimNextHydratableSuspenseInstance,
    
  912.   prepareToHydrateHostInstance,
    
  913.   prepareToHydrateHostTextInstance,
    
  914.   prepareToHydrateHostSuspenseInstance,
    
  915.   popHydrationState,
    
  916.   hasUnhydratedTailNodes,
    
  917.   warnIfUnhydratedTailNodes,
    
  918. };