Skip to content

Commit c10010a

Browse files
authored
[Fizz] Gracefully handle suspending in DOM configs (#26768)
E.g. if we suspend (throw a promise) in pushStartInstance today we might have already pushed some chunks (or even child segments potentially). We should revert back to where we were. This doesn't usually happen because when we suspend in a component it doesn't write anything itself, it'll always defer to som host instance to do the writing. There was a todo about this already but I'm not 100% sure it's always safe when suspending. It should be safe when suspending just regularly because it's just a noop. We might not even want "throwing a promise" in this mechanism to be supported longer term but for now that's how a suspend in internals.
1 parent f533cee commit c10010a

File tree

1 file changed

+15
-1
lines changed

1 file changed

+15
-1
lines changed

packages/react-server/src/ReactFizzServer.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1603,8 +1603,11 @@ function spawnNewSuspendedTask(
16031603
// This is a non-destructive form of rendering a node. If it suspends it spawns
16041604
// a new task and restores the context of this task to what it was before.
16051605
function renderNode(request: Request, task: Task, node: ReactNodeList): void {
1606-
// TODO: Store segment.children.length here and reset it in case something
1606+
// Store how much we've pushed at this point so we can reset it in case something
16071607
// suspended partially through writing something.
1608+
const segment = task.blockedSegment;
1609+
const childrenLength = segment.children.length;
1610+
const chunkLength = segment.chunks.length;
16081611

16091612
// Snapshot the current context in case something throws to interrupt the
16101613
// process.
@@ -1620,6 +1623,10 @@ function renderNode(request: Request, task: Task, node: ReactNodeList): void {
16201623
} catch (thrownValue) {
16211624
resetHooksState();
16221625

1626+
// Reset the write pointers to where we started.
1627+
segment.children.length = childrenLength;
1628+
segment.chunks.length = chunkLength;
1629+
16231630
const x =
16241631
thrownValue === SuspenseException
16251632
? // This is a special type of exception used for Suspense. For historical
@@ -1895,6 +1902,9 @@ function retryTask(request: Request, task: Task): void {
18951902
prevTaskInDEV = currentTaskInDEV;
18961903
currentTaskInDEV = task;
18971904
}
1905+
1906+
const childrenLength = segment.children.length;
1907+
const chunkLength = segment.chunks.length;
18981908
try {
18991909
// We call the destructive form that mutates this task. That way if something
19001910
// suspends again, we can reuse the same task instead of spawning a new one.
@@ -1919,6 +1929,10 @@ function retryTask(request: Request, task: Task): void {
19191929
} catch (thrownValue) {
19201930
resetHooksState();
19211931

1932+
// Reset the write pointers to where we started.
1933+
segment.children.length = childrenLength;
1934+
segment.chunks.length = chunkLength;
1935+
19221936
const x =
19231937
thrownValue === SuspenseException
19241938
? // This is a special type of exception used for Suspense. For historical

0 commit comments

Comments
 (0)