66
} from './ReactChildFiber';
67
import {processUpdateQueue} from './ReactUpdateQueue';
68
import {NoWork, Never} from './ReactFiberExpirationTime';
69
-import {ConcurrentMode, StrictMode} from './ReactTypeOfMode';
69
+import {ConcurrentMode, StrictMode, NoContext} from './ReactTypeOfMode';
70
import {
71
shouldSetTextContent,
72
shouldDeprioritizeSubtree,
1071
// We should attempt to render the primary children unless this boundary
1072
// already suspended during this render (`alreadyCaptured` is true).
1073
let nextState: SuspenseState | null = workInProgress.memoizedState;
1074
- if (nextState === null) {
1075
- // An empty suspense state means this boundary has not yet timed out.
1074
+
1075
+ let nextDidTimeout;
1076
+ if ((workInProgress.effectTag & DidCapture) === NoEffect) {
1077
+ // This is the first attempt.
1078
+ nextState = null;
1079
+ nextDidTimeout = false;
1080
} else {
1077
- if (!nextState.alreadyCaptured) {
1078
- // Since we haven't already suspended during this commit, clear the
1079
- // existing suspense state. We'll try rendering again.
1080
- nextState = null;
1081
- } else {
1082
- // Something in this boundary's subtree already suspended. Switch to
1083
- // rendering the fallback children. Set `alreadyCaptured` to true.
1084
- if (current !== null && nextState === current.memoizedState) {
1085
- // Create a new suspense state to avoid mutating the current tree's.
1086
- nextState = {
1087
- alreadyCaptured: true,
1088
- didTimeout: true,
1089
- timedOutAt: nextState.timedOutAt,
1090
- };
1091
- } else {
1092
- // Already have a clone, so it's safe to mutate.
1093
- nextState.alreadyCaptured = true;
1094
- nextState.didTimeout = true;
1095
- }
1096
- }
1081
+ // Something in this boundary's subtree already suspended. Switch to
1082
+ // rendering the fallback children.
1083
+ nextState = {
1084
+ timedOutAt: nextState !== null ? nextState.timedOutAt : NoWork,
1085
+ };
1086
+ nextDidTimeout = true;
1087
+ workInProgress.effectTag &= ~DidCapture;
1088
}
1098
- const nextDidTimeout = nextState !== null && nextState.didTimeout;
1089
1090
// This next part is a bit confusing. If the children timeout, we switch to
1091
// showing the fallback children in place of the "primary" children.
1130
NoWork,
1131
null,
1132
);
1133
+
1134
+ if ((workInProgress.mode & ConcurrentMode) === NoContext) {
1135
+ // Outside of concurrent mode, we commit the effects from the
1136
+ // partially completed, timed-out tree, too.
1137
+ const progressedState: SuspenseState = workInProgress.memoizedState;
1138
+ const progressedPrimaryChild: Fiber | null =
1139
+ progressedState !== null
1140
+ ? (workInProgress.child: any).child
1141
+ : (workInProgress.child: any);
1142
+ reuseProgressedPrimaryChild(
1143
+ workInProgress,
1144
+ primaryChildFragment,
1145
+ progressedPrimaryChild,
1146
+ );
1147
+ }
1148
+
1149
const fallbackChildFragment = createFiberFromFragment(
1150
nextFallbackChildren,
1151
mode,
1172
// This is an update. This branch is more complicated because we need to
1173
// ensure the state of the primary children is preserved.
1174
const prevState = current.memoizedState;
1169
- const prevDidTimeout = prevState !== null && prevState.didTimeout;
1175
+ const prevDidTimeout = prevState !== null;
1176
if (prevDidTimeout) {
1177
// The current tree already timed out. That means each child set is
1178
// wrapped in a fragment fiber.
1188
NoWork,
1189
);
1190
primaryChildFragment.effectTag |= Placement;
1191
+
1192
+ if ((workInProgress.mode & ConcurrentMode) === NoContext) {
1193
+ // Outside of concurrent mode, we commit the effects from the
1194
+ // partially completed, timed-out tree, too.
1195
+ const progressedState: SuspenseState = workInProgress.memoizedState;
1196
+ const progressedPrimaryChild: Fiber | null =
1197
+ progressedState !== null
1198
+ ? (workInProgress.child: any).child
1199
+ : (workInProgress.child: any);
1200
+ if (progressedPrimaryChild !== currentPrimaryChildFragment.child) {
1201
+ reuseProgressedPrimaryChild(
1202
+ workInProgress,
1203
+ primaryChildFragment,
1204
+ progressedPrimaryChild,
1205
+ );
1206
+ }
1207
+ }
1208
+
1209
// Clone the fallback child fragment, too. These we'll continue
1210
// working on.
1211
const fallbackChildFragment = (primaryChildFragment.sibling = createWorkInProgress(
1261
primaryChildFragment.effectTag |= Placement;
1262
primaryChildFragment.child = currentPrimaryChild;
1263
currentPrimaryChild.return = primaryChildFragment;
1264
+
1265
+ if ((workInProgress.mode & ConcurrentMode) === NoContext) {
1266
+ // Outside of concurrent mode, we commit the effects from the
1267
+ // partially completed, timed-out tree, too.
1268
+ const progressedState: SuspenseState = workInProgress.memoizedState;
1269
+ const progressedPrimaryChild: Fiber | null =
1270
+ progressedState !== null
1271
+ ? (workInProgress.child: any).child
1272
+ : (workInProgress.child: any);
1273
+ reuseProgressedPrimaryChild(
1274
+ workInProgress,
1275
+ primaryChildFragment,
1276
+ progressedPrimaryChild,
1277
+ );
1278
+ }
1279
+
1280
// Create a fragment from the fallback children, too.
1281
const fallbackChildFragment = (primaryChildFragment.sibling = createFiberFromFragment(
1282
nextFallbackChildren,
1310
return next;
1311
}
1312
1313
+function reuseProgressedPrimaryChild(
1314
+ workInProgress: Fiber,
1315
+ primaryChildFragment: Fiber,
1316
+ progressedChild: Fiber | null,
1317
+) {
1318
+ // This function is only called outside concurrent mode. Usually, if a work-
1319
+ // in-progress primary tree suspends, we throw it out and revert back to
1320
+ // current. Outside concurrent mode, though, we commit the suspended work-in-
1321
+ // progress, even though it didn't complete. This function reuses the children
1322
+ // and transfers the effects.
1323
+ let child = (primaryChildFragment.child = progressedChild);
1324
+ while (child !== null) {
1325
+ // Ensure that the first and last effect of the parent corresponds
1326
+ // to the children's first and last effect.
1327
+ if (primaryChildFragment.firstEffect === null) {
1328
+ primaryChildFragment.firstEffect = child.firstEffect;
1329
+ }
1330
+ if (child.lastEffect !== null) {
1331
+ if (primaryChildFragment.lastEffect !== null) {
1332
+ primaryChildFragment.lastEffect.nextEffect = child.firstEffect;
1333
+ }
1334
+ primaryChildFragment.lastEffect = child.lastEffect;
1335
+ }
1336
+
1337
+ // Append all the effects of the subtree and this fiber onto the effect
1338
+ // list of the parent. The completion order of the children affects the
1339
+ // side-effect order.
1340
+ if (child.effectTag > PerformedWork) {
1341
+ if (primaryChildFragment.lastEffect !== null) {
1342
+ primaryChildFragment.lastEffect.nextEffect = child;
1343
+ } else {
1344
+ primaryChildFragment.firstEffect = child;
1345
+ }
1346
+ primaryChildFragment.lastEffect = child;
1347
+ }
1348
+ child.return = primaryChildFragment;
1349
+ child = child.sibling;
1350
+ }
1351
+
1352
+ workInProgress.firstEffect = primaryChildFragment.firstEffect;
1353
+ workInProgress.lastEffect = primaryChildFragment.lastEffect;
1354
+}
1355
+
1356
function updatePortalComponent(
1357
current: Fiber | null,
1358
workInProgress: Fiber,
1509
return workInProgress.child;
1510
}
1511
1429
-/*
1430
- function reuseChildrenEffects(returnFiber : Fiber, firstChild : Fiber) {
1431
- let child = firstChild;
1432
- do {
1433
- // Ensure that the first and last effect of the parent corresponds
1434
- // to the children's first and last effect.
1435
- if (!returnFiber.firstEffect) {
1436
- returnFiber.firstEffect = child.firstEffect;
1437
- }
1438
- if (child.lastEffect) {
1439
- if (returnFiber.lastEffect) {
1440
- returnFiber.lastEffect.nextEffect = child.firstEffect;
1441
- }
1442
- returnFiber.lastEffect = child.lastEffect;
1443
- }
1444
- } while (child = child.sibling);
1445
- }
1446
- */
1447
-
1512
function bailoutOnAlreadyFinishedWork(
1513
current: Fiber | null,
1514
workInProgress: Fiber,
1592
break;
1593
case SuspenseComponent: {
1594
const state: SuspenseState | null = workInProgress.memoizedState;
1531
- const didTimeout = state !== null && state.didTimeout;
1595
+ const didTimeout = state !== null;
1596
if (didTimeout) {
1597
// If this boundary is currently timed out, we need to decide
1598
// whether to retry the primary children, or to skip over it and