Skip to content

Commit

Permalink
Gracefully handle suspending in DOM configs
Browse files Browse the repository at this point in the history
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.

There was a todo about this already but I'm not 100% sure it's always safe.
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.
  • Loading branch information
sebmarkbage committed May 3, 2023
1 parent 388686f commit 804dced
Showing 1 changed file with 15 additions and 1 deletion.
16 changes: 15 additions & 1 deletion packages/react-server/src/ReactFizzServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1603,8 +1603,11 @@ function spawnNewSuspendedTask(
// This is a non-destructive form of rendering a node. If it suspends it spawns
// a new task and restores the context of this task to what it was before.
function renderNode(request: Request, task: Task, node: ReactNodeList): void {
// TODO: Store segment.children.length here and reset it in case something
// Store how much we've pushed at this point so we can reset it in case something
// suspended partially through writing something.
const segment = task.blockedSegment;
const childrenLength = segment.children.length;
const chunkLength = segment.chunks.length;

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

// Reset the write pointers to where we started.
segment.children.length = childrenLength;
segment.chunks.length = chunkLength;

const x =
thrownValue === SuspenseException
? // This is a special type of exception used for Suspense. For historical
Expand Down Expand Up @@ -1895,6 +1902,9 @@ function retryTask(request: Request, task: Task): void {
prevTaskInDEV = currentTaskInDEV;
currentTaskInDEV = task;
}

const childrenLength = segment.children.length;
const chunkLength = segment.chunks.length;
try {
// We call the destructive form that mutates this task. That way if something
// suspends again, we can reuse the same task instead of spawning a new one.
Expand All @@ -1919,6 +1929,10 @@ function retryTask(request: Request, task: Task): void {
} catch (thrownValue) {
resetHooksState();

// Reset the write pointers to where we started.
segment.children.length = childrenLength;
segment.chunks.length = chunkLength;

const x =
thrownValue === SuspenseException
? // This is a special type of exception used for Suspense. For historical
Expand Down

0 comments on commit 804dced

Please sign in to comment.