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 {ReactNodeList, Wakeable} from 'shared/ReactTypes';
    
  11. import type {Fiber} from './ReactInternalTypes';
    
  12. import type {SuspenseInstance} from './ReactFiberConfig';
    
  13. import type {Lane} from './ReactFiberLane';
    
  14. import type {TreeContext} from './ReactFiberTreeContext';
    
  15. 
    
  16. import {SuspenseComponent, SuspenseListComponent} from './ReactWorkTags';
    
  17. import {NoFlags, DidCapture} from './ReactFiberFlags';
    
  18. import {
    
  19.   isSuspenseInstancePending,
    
  20.   isSuspenseInstanceFallback,
    
  21. } from './ReactFiberConfig';
    
  22. 
    
  23. export type SuspenseProps = {
    
  24.   children?: ReactNodeList,
    
  25.   fallback?: ReactNodeList,
    
  26. 
    
  27.   // TODO: Add "unstable_" prefix?
    
  28.   suspenseCallback?: (Set<Wakeable> | null) => mixed,
    
  29. 
    
  30.   unstable_avoidThisFallback?: boolean,
    
  31.   unstable_expectedLoadTime?: number,
    
  32.   unstable_name?: string,
    
  33. };
    
  34. 
    
  35. // A null SuspenseState represents an unsuspended normal Suspense boundary.
    
  36. // A non-null SuspenseState means that it is blocked for one reason or another.
    
  37. // - A non-null dehydrated field means it's blocked pending hydration.
    
  38. //   - A non-null dehydrated field can use isSuspenseInstancePending or
    
  39. //     isSuspenseInstanceFallback to query the reason for being dehydrated.
    
  40. // - A null dehydrated field means it's blocked by something suspending and
    
  41. //   we're currently showing a fallback instead.
    
  42. export type SuspenseState = {
    
  43.   // If this boundary is still dehydrated, we store the SuspenseInstance
    
  44.   // here to indicate that it is dehydrated (flag) and for quick access
    
  45.   // to check things like isSuspenseInstancePending.
    
  46.   dehydrated: null | SuspenseInstance,
    
  47.   treeContext: null | TreeContext,
    
  48.   // Represents the lane we should attempt to hydrate a dehydrated boundary at.
    
  49.   // OffscreenLane is the default for dehydrated boundaries.
    
  50.   // NoLane is the default for normal boundaries, which turns into "normal" pri.
    
  51.   retryLane: Lane,
    
  52. };
    
  53. 
    
  54. export type SuspenseListTailMode = 'collapsed' | 'hidden' | void;
    
  55. 
    
  56. export type SuspenseListRenderState = {
    
  57.   isBackwards: boolean,
    
  58.   // The currently rendering tail row.
    
  59.   rendering: null | Fiber,
    
  60.   // The absolute time when we started rendering the most recent tail row.
    
  61.   renderingStartTime: number,
    
  62.   // The last of the already rendered children.
    
  63.   last: null | Fiber,
    
  64.   // Remaining rows on the tail of the list.
    
  65.   tail: null | Fiber,
    
  66.   // Tail insertions setting.
    
  67.   tailMode: SuspenseListTailMode,
    
  68. };
    
  69. 
    
  70. export type RetryQueue = Set<Wakeable>;
    
  71. 
    
  72. export function findFirstSuspended(row: Fiber): null | Fiber {
    
  73.   let node = row;
    
  74.   while (node !== null) {
    
  75.     if (node.tag === SuspenseComponent) {
    
  76.       const state: SuspenseState | null = node.memoizedState;
    
  77.       if (state !== null) {
    
  78.         const dehydrated: null | SuspenseInstance = state.dehydrated;
    
  79.         if (
    
  80.           dehydrated === null ||
    
  81.           isSuspenseInstancePending(dehydrated) ||
    
  82.           isSuspenseInstanceFallback(dehydrated)
    
  83.         ) {
    
  84.           return node;
    
  85.         }
    
  86.       }
    
  87.     } else if (
    
  88.       node.tag === SuspenseListComponent &&
    
  89.       // revealOrder undefined can't be trusted because it don't
    
  90.       // keep track of whether it suspended or not.
    
  91.       node.memoizedProps.revealOrder !== undefined
    
  92.     ) {
    
  93.       const didSuspend = (node.flags & DidCapture) !== NoFlags;
    
  94.       if (didSuspend) {
    
  95.         return node;
    
  96.       }
    
  97.     } else if (node.child !== null) {
    
  98.       node.child.return = node;
    
  99.       node = node.child;
    
  100.       continue;
    
  101.     }
    
  102.     if (node === row) {
    
  103.       return null;
    
  104.     }
    
  105.     while (node.sibling === null) {
    
  106.       if (node.return === null || node.return === row) {
    
  107.         return null;
    
  108.       }
    
  109.       node = node.return;
    
  110.     }
    
  111.     node.sibling.return = node.return;
    
  112.     node = node.sibling;
    
  113.   }
    
  114.   return null;
    
  115. }