forked from facebook/react
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathReactFiberSuspenseComponent.js
106 lines (99 loc) · 3.65 KB
/
ReactFiberSuspenseComponent.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type {Wakeable} from 'shared/ReactTypes';
import type {Fiber} from './ReactInternalTypes';
import type {SuspenseInstance} from './ReactFiberConfig';
import type {Lane} from './ReactFiberLane';
import type {TreeContext} from './ReactFiberTreeContext';
import type {CapturedValue} from './ReactCapturedValue';
import {SuspenseComponent, SuspenseListComponent} from './ReactWorkTags';
import {NoFlags, DidCapture} from './ReactFiberFlags';
import {
isSuspenseInstancePending,
isSuspenseInstanceFallback,
} from './ReactFiberConfig';
// A null SuspenseState represents an unsuspended normal Suspense boundary.
// A non-null SuspenseState means that it is blocked for one reason or another.
// - A non-null dehydrated field means it's blocked pending hydration.
// - A non-null dehydrated field can use isSuspenseInstancePending or
// isSuspenseInstanceFallback to query the reason for being dehydrated.
// - A null dehydrated field means it's blocked by something suspending and
// we're currently showing a fallback instead.
export type SuspenseState = {
// If this boundary is still dehydrated, we store the SuspenseInstance
// here to indicate that it is dehydrated (flag) and for quick access
// to check things like isSuspenseInstancePending.
dehydrated: null | SuspenseInstance,
treeContext: null | TreeContext,
// Represents the lane we should attempt to hydrate a dehydrated boundary at.
// OffscreenLane is the default for dehydrated boundaries.
// NoLane is the default for normal boundaries, which turns into "normal" pri.
retryLane: Lane,
// Stashed Errors that happened while attempting to hydrate this boundary.
hydrationErrors: Array<CapturedValue<mixed>> | null,
};
export type SuspenseListTailMode = 'collapsed' | 'hidden' | void;
export type SuspenseListRenderState = {
isBackwards: boolean,
// The currently rendering tail row.
rendering: null | Fiber,
// The absolute time when we started rendering the most recent tail row.
renderingStartTime: number,
// The last of the already rendered children.
last: null | Fiber,
// Remaining rows on the tail of the list.
tail: null | Fiber,
// Tail insertions setting.
tailMode: SuspenseListTailMode,
};
export type RetryQueue = Set<Wakeable>;
export function findFirstSuspended(row: Fiber): null | Fiber {
let node = row;
while (node !== null) {
if (node.tag === SuspenseComponent) {
const state: SuspenseState | null = node.memoizedState;
if (state !== null) {
const dehydrated: null | SuspenseInstance = state.dehydrated;
if (
dehydrated === null ||
isSuspenseInstancePending(dehydrated) ||
isSuspenseInstanceFallback(dehydrated)
) {
return node;
}
}
} else if (
node.tag === SuspenseListComponent &&
// revealOrder undefined can't be trusted because it don't
// keep track of whether it suspended or not.
node.memoizedProps.revealOrder !== undefined
) {
const didSuspend = (node.flags & DidCapture) !== NoFlags;
if (didSuspend) {
return node;
}
} else if (node.child !== null) {
node.child.return = node;
node = node.child;
continue;
}
if (node === row) {
return null;
}
while (node.sibling === null) {
if (node.return === null || node.return === row) {
return null;
}
node = node.return;
}
node.sibling.return = node.return;
node = node.sibling;
}
return null;
}