Skip to content

Commit 104995f

Browse files
committed
Aborting a replay task at the root should still complete the shell
1 parent 35f1d0f commit 104995f

File tree

2 files changed

+73
-6
lines changed

2 files changed

+73
-6
lines changed

packages/react-dom/src/__tests__/ReactDOMFizzStaticBrowser-test.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,4 +1065,65 @@ describe('ReactDOMFizzStaticBrowser', () => {
10651065
<div>Loading...</div>,
10661066
]);
10671067
});
1068+
1069+
// @gate enablePostpone
1070+
it('can abort the resume', async () => {
1071+
let prerendering = true;
1072+
const infinitePromise = new Promise(() => {});
1073+
function Postpone() {
1074+
if (prerendering) {
1075+
React.unstable_postpone();
1076+
}
1077+
return 'Hello';
1078+
}
1079+
1080+
function App() {
1081+
if (!prerendering) {
1082+
React.use(infinitePromise);
1083+
}
1084+
return (
1085+
<div>
1086+
<Suspense fallback="Loading...">
1087+
<Postpone />
1088+
</Suspense>
1089+
</div>
1090+
);
1091+
}
1092+
1093+
const prerendered = await ReactDOMFizzStatic.prerender(<App />);
1094+
expect(prerendered.postponed).not.toBe(null);
1095+
1096+
await readIntoContainer(prerendered.prelude);
1097+
1098+
expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
1099+
1100+
prerendering = false;
1101+
1102+
const controller = new AbortController();
1103+
1104+
const errors = [];
1105+
1106+
const resumedPromise = ReactDOMFizzServer.resume(
1107+
<App />,
1108+
JSON.parse(JSON.stringify(prerendered.postponed)),
1109+
{
1110+
signal: controller.signal,
1111+
onError(x) {
1112+
errors.push(x);
1113+
},
1114+
},
1115+
);
1116+
1117+
controller.abort('abort');
1118+
1119+
const resumed = await resumedPromise;
1120+
await resumed.allReady;
1121+
1122+
expect(errors).toEqual(['abort']);
1123+
1124+
await readIntoContainer(resumed);
1125+
1126+
// Client rendered
1127+
expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
1128+
});
10681129
});

packages/react-server/src/ReactFizzServer.js

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3088,14 +3088,14 @@ function abortTask(task: Task, request: Request, error: mixed): void {
30883088
}
30893089

30903090
if (boundary === null) {
3091-
request.allPendingTasks--;
30923091
if (request.status !== CLOSING && request.status !== CLOSED) {
30933092
const replay: null | ReplaySet = task.replay;
30943093
if (replay === null) {
30953094
// We didn't complete the root so we have nothing to show. We can close
30963095
// the request;
30973096
logRecoverableError(request, error);
30983097
fatalError(request, error);
3098+
return;
30993099
} else {
31003100
// If the shell aborts during a replay, that's not a fatal error. Instead
31013101
// we should be able to recover by client rendering all the root boundaries in
@@ -3112,6 +3112,12 @@ function abortTask(task: Task, request: Request, error: mixed): void {
31123112
errorDigest,
31133113
);
31143114
}
3115+
request.pendingRootTasks--;
3116+
if (request.pendingRootTasks === 0) {
3117+
request.onShellError = noop;
3118+
const onShellReady = request.onShellReady;
3119+
onShellReady();
3120+
}
31153121
}
31163122
}
31173123
} else {
@@ -3148,12 +3154,12 @@ function abortTask(task: Task, request: Request, error: mixed): void {
31483154
abortTask(fallbackTask, request, error),
31493155
);
31503156
boundary.fallbackAbortableTasks.clear();
3157+
}
31513158

3152-
request.allPendingTasks--;
3153-
if (request.allPendingTasks === 0) {
3154-
const onAllReady = request.onAllReady;
3155-
onAllReady();
3156-
}
3159+
request.allPendingTasks--;
3160+
if (request.allPendingTasks === 0) {
3161+
const onAllReady = request.onAllReady;
3162+
onAllReady();
31573163
}
31583164
}
31593165

0 commit comments

Comments
 (0)