Skip to content

Commit f7d041a

Browse files
committed
Abort cacheSignal() when the render fatals or completes successfully
1 parent 4a72faf commit f7d041a

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed

packages/react-reconciler/src/__tests__/ReactCache-test.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,42 @@ describe('ReactCache', () => {
227227
expect(cacheSignal()).toBe(null);
228228
});
229229

230+
it('cacheSignal() aborts when the render finishes normally', async () => {
231+
let renderedCacheSignal = null;
232+
233+
let resolve;
234+
const promise = new Promise(r => (resolve = r));
235+
236+
async function Test() {
237+
renderedCacheSignal = cacheSignal();
238+
await promise;
239+
return 'Hi';
240+
}
241+
242+
const controller = new AbortController();
243+
const errors = [];
244+
const result = ReactNoopFlightServer.render(<Test />, {
245+
signal: controller.signal,
246+
onError(x) {
247+
errors.push(x);
248+
},
249+
});
250+
expect(errors).toEqual([]);
251+
expect(renderedCacheSignal).not.toBe(controller.signal); // In the future we might make these the same
252+
expect(renderedCacheSignal.aborted).toBe(false);
253+
await resolve();
254+
await 0;
255+
await 0;
256+
257+
expect(await ReactNoopFlightClient.read(result)).toBe('Hi');
258+
259+
expect(errors).toEqual([]);
260+
expect(renderedCacheSignal.aborted).toBe(true);
261+
expect(renderedCacheSignal.reason.message).toContain(
262+
'This render completed successfully.',
263+
);
264+
});
265+
230266
it('cacheSignal() aborts when the render is aborted', async () => {
231267
let renderedCacheSignal = null;
232268

packages/react-server/src/ReactFlightServer.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3371,6 +3371,13 @@ function fatalError(request: Request, error: mixed): void {
33713371
request.status = CLOSING;
33723372
request.fatalError = error;
33733373
}
3374+
const abortReason = new Error(
3375+
'The render was aborted due to a fatal error.',
3376+
{
3377+
cause: error,
3378+
},
3379+
);
3380+
request.cacheController.abort(abortReason);
33743381
}
33753382

33763383
function emitPostponeChunk(
@@ -4842,6 +4849,12 @@ function flushCompletedChunks(
48424849
if (enableTaint) {
48434850
cleanupTaintQueue(request);
48444851
}
4852+
if (request.status < ABORTING) {
4853+
const abortReason = new Error(
4854+
'This render completed successfully. All cacheSignals are now aborted to allow clean up of any unused resources.',
4855+
);
4856+
request.cacheController.abort(abortReason);
4857+
}
48454858
request.status = CLOSED;
48464859
close(destination);
48474860
request.destination = null;
@@ -4923,8 +4936,8 @@ export function abort(request: Request, reason: mixed): void {
49234936
// We define any status below OPEN as OPEN equivalent
49244937
if (request.status <= OPEN) {
49254938
request.status = ABORTING;
4939+
request.cacheController.abort(reason);
49264940
}
4927-
request.cacheController.abort(reason);
49284941
const abortableTasks = request.abortableTasks;
49294942
if (abortableTasks.size > 0) {
49304943
if (enableHalt && request.type === PRERENDER) {

scripts/error-codes/codes.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,5 +546,7 @@
546546
"558": "Client rendering an Activity suspended it again. This is a bug in React.",
547547
"559": "Expected to find a host node. This is a bug in React.",
548548
"560": "Cannot use a startGestureTransition() with a comment node root.",
549-
"561": "This rendered a large document (>%s kB) without any Suspense boundaries around most of it. That can delay initial paint longer than necessary. To improve load performance, add a <Suspense> or <SuspenseList> around the content you expect to be below the header or below the fold. In the meantime, the content will deopt to paint arbitrary incomplete pieces of HTML."
549+
"561": "This rendered a large document (>%s kB) without any Suspense boundaries around most of it. That can delay initial paint longer than necessary. To improve load performance, add a <Suspense> or <SuspenseList> around the content you expect to be below the header or below the fold. In the meantime, the content will deopt to paint arbitrary incomplete pieces of HTML.",
550+
"562": "The render was aborted due to a fatal error.",
551+
"563": "This render completed successfully. All cacheSignals are now aborted to allow clean up of any unused resources."
550552
}

0 commit comments

Comments
 (0)