Skip to content

Commit 500a056

Browse files
committed
[DevTools] Unmount fallbacks in the context of the parent Suspense
1 parent c4f7e09 commit 500a056

File tree

2 files changed

+66
-3
lines changed

2 files changed

+66
-3
lines changed

packages/react-devtools-shared/src/__tests__/store-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3082,7 +3082,7 @@ describe('Store', () => {
30823082

30833083
await actAsync(() => render(null));
30843084

3085-
expect(store).toMatchInlineSnapshot();
3085+
expect(store).toMatchInlineSnapshot(``);
30863086
});
30873087

30883088
it('should handle an empty root', async () => {

packages/react-devtools-shared/src/backend/fiber/renderer.js

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3070,6 +3070,33 @@ export function attach(
30703070
}
30713071
}
30723072
3073+
function unmountSuspenseChildrenRecursively(
3074+
contentInstance: DevToolsInstance,
3075+
stashedSuspenseParent: null | SuspenseNode,
3076+
stashedSuspensePrevious: null | SuspenseNode,
3077+
stashedSuspenseRemaining: null | SuspenseNode,
3078+
): void {
3079+
// First unmount only the Offscreen boundary. I.e. the main content.
3080+
unmountInstanceRecursively(contentInstance);
3081+
3082+
// Next, we'll pop back out of the SuspenseNode that we added above and now we'll
3083+
// unmount the fallback, unmounting anything in the context of the parent SuspenseNode.
3084+
// Since the fallback conceptually blocks the parent.
3085+
reconcilingParentSuspenseNode = stashedSuspenseParent;
3086+
previouslyReconciledSiblingSuspenseNode = stashedSuspensePrevious;
3087+
remainingReconcilingChildrenSuspenseNodes = stashedSuspenseRemaining;
3088+
const fallbackInstance = remainingReconcilingChildren;
3089+
if (fallbackInstance !== null) {
3090+
unmountInstanceRecursively(fallbackInstance);
3091+
3092+
if (remainingReconcilingChildren !== null) {
3093+
throw new Error(
3094+
'There should be no instances left to unmount after a Suspense fallback was unmounted.',
3095+
);
3096+
}
3097+
}
3098+
}
3099+
30733100
function isChildOf(
30743101
parentInstance: DevToolsInstance,
30753102
childInstance: DevToolsInstance,
@@ -4015,6 +4042,7 @@ export function attach(
40154042
debug('unmountInstanceRecursively()', instance, reconcilingParent);
40164043
}
40174044
4045+
let shouldPopSuspenseNode = false;
40184046
const stashedParent = reconcilingParent;
40194047
const stashedPrevious = previouslyReconciledSibling;
40204048
const stashedRemaining = remainingReconcilingChildren;
@@ -4035,11 +4063,46 @@ export function attach(
40354063
previouslyReconciledSiblingSuspenseNode = null;
40364064
remainingReconcilingChildrenSuspenseNodes =
40374065
instance.suspenseNode.firstChild;
4066+
4067+
shouldPopSuspenseNode = true;
40384068
}
40394069
40404070
try {
40414071
// Unmount the remaining set.
4042-
unmountRemainingChildren();
4072+
if (
4073+
(instance.kind === FIBER_INSTANCE ||
4074+
instance.kind === FILTERED_FIBER_INSTANCE) &&
4075+
instance.data.tag === SuspenseComponent &&
4076+
OffscreenComponent !== -1
4077+
) {
4078+
const fiber = instance.data;
4079+
const contentFiberInstance = remainingReconcilingChildren;
4080+
const hydrated = isFiberHydrated(fiber);
4081+
if (hydrated) {
4082+
if (contentFiberInstance === null) {
4083+
throw new Error(
4084+
'There should always be an Offscreen Fiber child in a hydrated Suspense boundary.',
4085+
);
4086+
}
4087+
4088+
unmountSuspenseChildrenRecursively(
4089+
contentFiberInstance,
4090+
stashedSuspenseParent,
4091+
stashedSuspensePrevious,
4092+
stashedSuspenseRemaining,
4093+
);
4094+
// unmountSuspenseChildren already popped
4095+
shouldPopSuspenseNode = false;
4096+
} else {
4097+
if (contentFiberInstance !== null) {
4098+
throw new Error(
4099+
'A dehydrated Suspense node should not have a content Fiber.',
4100+
);
4101+
}
4102+
}
4103+
} else {
4104+
unmountRemainingChildren();
4105+
}
40434106
removePreviousSuspendedBy(
40444107
instance,
40454108
previousSuspendedBy,
@@ -4049,7 +4112,7 @@ export function attach(
40494112
reconcilingParent = stashedParent;
40504113
previouslyReconciledSibling = stashedPrevious;
40514114
remainingReconcilingChildren = stashedRemaining;
4052-
if (instance.suspenseNode !== null) {
4115+
if (shouldPopSuspenseNode) {
40534116
reconcilingParentSuspenseNode = stashedSuspenseParent;
40544117
previouslyReconciledSiblingSuspenseNode = stashedSuspensePrevious;
40554118
remainingReconcilingChildrenSuspenseNodes = stashedSuspenseRemaining;

0 commit comments

Comments
 (0)