251
const CommitContext = /* */ 0b0100;
252
export const RetryAfterError = /* */ 0b1000;
253
254
-type RootExitStatus = 0 | 1 | 2 | 3 | 4 | 5;
255
-const RootIncomplete = 0;
254
+type RootExitStatus = 0 | 1 | 2 | 3 | 4 | 5 | 6;
255
+const RootInProgress = 0;
256
const RootFatalErrored = 1;
257
const RootErrored = 2;
258
const RootSuspended = 3;
259
const RootSuspendedWithDelay = 4;
260
const RootCompleted = 5;
261
+const RootDidNotComplete = 6;
262
263
// Describes where we are in the React execution stack
264
let executionContext: ExecutionContext = NoContext;
281
const subtreeRenderLanesCursor: StackCursor<Lanes> = createCursor(NoLanes);
282
283
// Whether to root completed, errored, suspended, etc.
283
-let workInProgressRootExitStatus: RootExitStatus = RootIncomplete;
284
+let workInProgressRootExitStatus: RootExitStatus = RootInProgress;
285
// A fatal error, if one is thrown
286
let workInProgressRootFatalError: mixed = null;
287
// "Included" lanes refer to lanes that were worked on during this render. It's
818
let exitStatus = shouldTimeSlice
819
? renderRootConcurrent(root, lanes)
820
: renderRootSync(root, lanes);
820
- if (exitStatus !== RootIncomplete) {
821
+ if (exitStatus !== RootInProgress) {
822
if (exitStatus === RootErrored) {
823
// If something threw an error, try rendering one more time. We'll
824
// render synchronously to block concurrent data mutations, and we'll
838
throw fatalError;
839
}
840
840
- // Check if this render may have yielded to a concurrent event, and if so,
841
- // confirm that any newly rendered stores are consistent.
842
- // TODO: It's possible that even a concurrent render may never have yielded
843
- // to the main thread, if it was fast enough, or if it expired. We could
844
- // skip the consistency check in that case, too.
845
- const renderWasConcurrent = !includesBlockingLane(root, lanes);
846
- const finishedWork: Fiber = (root.current.alternate: any);
847
- if (
848
- renderWasConcurrent &&
849
- !isRenderConsistentWithExternalStores(finishedWork)
850
- ) {
851
- // A store was mutated in an interleaved event. Render again,
852
- // synchronously, to block further mutations.
853
- exitStatus = renderRootSync(root, lanes);
854
-
855
- // We need to check again if something threw
856
- if (exitStatus === RootErrored) {
857
- const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);
858
- if (errorRetryLanes !== NoLanes) {
859
- lanes = errorRetryLanes;
860
- exitStatus = recoverFromConcurrentError(root, errorRetryLanes);
861
- // We assume the tree is now consistent because we didn't yield to any
862
- // concurrent events.
841
+ if (exitStatus === RootDidNotComplete) {
842
+ // The render unwound without completing the tree. This happens in special
843
+ // cases where need to exit the current render without producing a
844
+ // consistent tree or committing.
845
+ //
846
+ // This should only happen during a concurrent render, not a discrete or
847
+ // synchronous update. We should have already checked for this when we
848
+ // unwound the stack.
849
+ markRootSuspended(root, lanes);
850
+ } else {
851
+ // The render completed.
852
+
853
+ // Check if this render may have yielded to a concurrent event, and if so,
854
+ // confirm that any newly rendered stores are consistent.
855
+ // TODO: It's possible that even a concurrent render may never have yielded
856
+ // to the main thread, if it was fast enough, or if it expired. We could
857
+ // skip the consistency check in that case, too.
858
+ const renderWasConcurrent = !includesBlockingLane(root, lanes);
859
+ const finishedWork: Fiber = (root.current.alternate: any);
860
+ if (
861
+ renderWasConcurrent &&
862
+ !isRenderConsistentWithExternalStores(finishedWork)
863
+ ) {
864
+ // A store was mutated in an interleaved event. Render again,
865
+ // synchronously, to block further mutations.
866
+ exitStatus = renderRootSync(root, lanes);
867
+
868
+ // We need to check again if something threw
869
+ if (exitStatus === RootErrored) {
870
+ const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);
871
+ if (errorRetryLanes !== NoLanes) {
872
+ lanes = errorRetryLanes;
873
+ exitStatus = recoverFromConcurrentError(root, errorRetryLanes);
874
+ // We assume the tree is now consistent because we didn't yield to any
875
+ // concurrent events.
876
+ }
877
+ }
878
+ if (exitStatus === RootFatalErrored) {
879
+ const fatalError = workInProgressRootFatalError;
880
+ prepareFreshStack(root, NoLanes);
881
+ markRootSuspended(root, lanes);
882
+ ensureRootIsScheduled(root, now());
883
+ throw fatalError;
884
}
885
}
865
- if (exitStatus === RootFatalErrored) {
866
- const fatalError = workInProgressRootFatalError;
867
- prepareFreshStack(root, NoLanes);
868
- markRootSuspended(root, lanes);
869
- ensureRootIsScheduled(root, now());
870
- throw fatalError;
871
- }
872
- }
886
874
- // We now have a consistent tree. The next step is either to commit it,
875
- // or, if something suspended, wait to commit it after a timeout.
876
- root.finishedWork = finishedWork;
877
- root.finishedLanes = lanes;
878
- finishConcurrentRender(root, exitStatus, lanes);
887
+ // We now have a consistent tree. The next step is either to commit it,
888
+ // or, if something suspended, wait to commit it after a timeout.
889
+ root.finishedWork = finishedWork;
890
+ root.finishedLanes = lanes;
891
+ finishConcurrentRender(root, exitStatus, lanes);
892
+ }
893
}
894
895
ensureRootIsScheduled(root, now());
946
947
function finishConcurrentRender(root, exitStatus, lanes) {
948
switch (exitStatus) {
935
- case RootIncomplete:
949
+ case RootInProgress:
950
case RootFatalErrored: {
951
throw new Error('Root did not complete. This is a bug in React.');
952
}
1162
throw fatalError;
1163
}
1164
1165
+ if (exitStatus === RootDidNotComplete) {
1166
+ throw new Error('Root did not complete. This is a bug in React.');
1167
+ }
1168
+
1169
// We now have a consistent tree. Because this is a sync render, we
1170
// will commit it even if something suspended.
1171
const finishedWork: Fiber = (root.current.alternate: any);
1361
workInProgressRoot = root;
1362
workInProgress = createWorkInProgress(root.current, null);
1363
workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;
1346
- workInProgressRootExitStatus = RootIncomplete;
1364
+ workInProgressRootExitStatus = RootInProgress;
1365
workInProgressRootFatalError = null;
1366
workInProgressRootSkippedLanes = NoLanes;
1367
workInProgressRootInterleavedUpdatedLanes = NoLanes;
1491
}
1492
1493
export function renderDidSuspend(): void {
1476
- if (workInProgressRootExitStatus === RootIncomplete) {
1494
+ if (workInProgressRootExitStatus === RootInProgress) {
1495
workInProgressRootExitStatus = RootSuspended;
1496
}
1497
}
1498
1499
export function renderDidSuspendDelayIfPossible(): void {
1500
if (
1483
- workInProgressRootExitStatus === RootIncomplete ||
1501
+ workInProgressRootExitStatus === RootInProgress ||
1502
workInProgressRootExitStatus === RootSuspended ||
1503
workInProgressRootExitStatus === RootErrored
1504
) {
1539
export function renderHasNotSuspendedYet(): boolean {
1540
// If something errored or completed, we can't really be sure,
1541
// so those are false.
1524
- return workInProgressRootExitStatus === RootIncomplete;
1542
+ return workInProgressRootExitStatus === RootInProgress;
1543
}
1544
1545
function renderRootSync(root: FiberRoot, lanes: Lanes) {
1689
if (enableSchedulingProfiler) {
1690
markRenderYielded();
1691
}
1674
- return RootIncomplete;
1692
+ return RootInProgress;
1693
} else {
1694
// Completed the tree.
1695
if (enableSchedulingProfiler) {
1814
returnFiber.flags |= Incomplete;
1815
returnFiber.subtreeFlags = NoFlags;
1816
returnFiber.deletions = null;
1817
+ } else {
1818
+ // We've unwound all the way to the root.
1819
+ workInProgressRootExitStatus = RootDidNotComplete;
1820
+ workInProgress = null;
1821
+ return;
1822
}
1823
}
1824
1835
} while (completedWork !== null);
1836
1837
// We've reached the root.
1815
- if (workInProgressRootExitStatus === RootIncomplete) {
1838
+ if (workInProgressRootExitStatus === RootInProgress) {
1839
workInProgressRootExitStatus = RootCompleted;
1840
}
1841
}