Skip to content

Commit ec2e4f2

Browse files
committed
Fix reentrancy bug
1 parent ea155e2 commit ec2e4f2

File tree

2 files changed

+15
-15
lines changed

2 files changed

+15
-15
lines changed

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -353,13 +353,16 @@ describe('ReactDOMFizzServer', () => {
353353

354354
await act(async () => {
355355
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
356-
<Suspense fallback={<Text text="Loading A..." />}>
357-
<>
358-
<Text text="This will show A: " />
359-
<div>
360-
<AsyncText text="A" />
361-
</div>
362-
</>
356+
// We use two nested boundaries to flush out coverage of an old reentrancy bug.
357+
<Suspense fallback="Loading...">
358+
<Suspense fallback={<Text text="Loading A..." />}>
359+
<>
360+
<Text text="This will show A: " />
361+
<div>
362+
<AsyncText text="A" />
363+
</div>
364+
</>
365+
</Suspense>
363366
</Suspense>,
364367
writableA,
365368
{

packages/react-server/src/ReactFizzServer.js

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,11 +1017,6 @@ function abortTask(task: Task): void {
10171017
} else {
10181018
boundary.pendingTasks--;
10191019

1020-
// If this boundary was still pending then we haven't already cancelled its fallbacks.
1021-
// We'll need to abort the fallbacks, which will also error that parent boundary.
1022-
boundary.fallbackAbortableTasks.forEach(abortTask, request);
1023-
boundary.fallbackAbortableTasks.clear();
1024-
10251020
if (!boundary.forceClientRender) {
10261021
boundary.forceClientRender = true;
10271022
if (boundary.parentFlushed) {
@@ -1060,9 +1055,6 @@ function finishedTask(
10601055
// This already errored.
10611056
} else if (boundary.pendingTasks === 0) {
10621057
// This must have been the last segment we were waiting on. This boundary is now complete.
1063-
// We can now cancel any pending task on the fallback since we won't need to show it anymore.
1064-
boundary.fallbackAbortableTasks.forEach(abortTaskSoft, request);
1065-
boundary.fallbackAbortableTasks.clear();
10661058
if (segment.parentFlushed) {
10671059
// Our parent segment already flushed, so we need to schedule this segment to be emitted.
10681060
boundary.completedSegments.push(segment);
@@ -1072,6 +1064,11 @@ function finishedTask(
10721064
// parent flushed, we need to schedule the boundary to be emitted.
10731065
request.completedBoundaries.push(boundary);
10741066
}
1067+
// We can now cancel any pending task on the fallback since we won't need to show it anymore.
1068+
// This needs to happen after we read the parentFlushed flags because aborting can finish
1069+
// work which can trigger user code, which can start flushing, which can change those flags.
1070+
boundary.fallbackAbortableTasks.forEach(abortTaskSoft, request);
1071+
boundary.fallbackAbortableTasks.clear();
10751072
} else {
10761073
if (segment.parentFlushed) {
10771074
// Our parent already flushed, so we need to schedule this segment to be emitted.

0 commit comments

Comments
 (0)