@@ -3070,6 +3070,24 @@ 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+ unmountRemainingChildren();
3089+ }
3090+
30733091 function isChildOf(
30743092 parentInstance: DevToolsInstance,
30753093 childInstance: DevToolsInstance,
@@ -4015,6 +4033,7 @@ export function attach(
40154033 debug('unmountInstanceRecursively()', instance, reconcilingParent);
40164034 }
40174035
4036+ let shouldPopSuspenseNode = false;
40184037 const stashedParent = reconcilingParent;
40194038 const stashedPrevious = previouslyReconciledSibling;
40204039 const stashedRemaining = remainingReconcilingChildren;
@@ -4035,11 +4054,46 @@ export function attach(
40354054 previouslyReconciledSiblingSuspenseNode = null;
40364055 remainingReconcilingChildrenSuspenseNodes =
40374056 instance.suspenseNode.firstChild;
4057+
4058+ shouldPopSuspenseNode = true;
40384059 }
40394060
40404061 try {
40414062 // Unmount the remaining set.
4042- unmountRemainingChildren();
4063+ if (
4064+ (instance.kind === FIBER_INSTANCE ||
4065+ instance.kind === FILTERED_FIBER_INSTANCE) &&
4066+ instance.data.tag === SuspenseComponent &&
4067+ OffscreenComponent !== -1
4068+ ) {
4069+ const fiber = instance.data;
4070+ const contentFiberInstance = remainingReconcilingChildren;
4071+ const hydrated = isFiberHydrated(fiber);
4072+ if (hydrated) {
4073+ if (contentFiberInstance === null) {
4074+ throw new Error(
4075+ 'There should always be an Offscreen Fiber child in a hydrated Suspense boundary.',
4076+ );
4077+ }
4078+
4079+ unmountSuspenseChildrenRecursively(
4080+ contentFiberInstance,
4081+ stashedSuspenseParent,
4082+ stashedSuspensePrevious,
4083+ stashedSuspenseRemaining,
4084+ );
4085+ // unmountSuspenseChildren already popped
4086+ shouldPopSuspenseNode = false;
4087+ } else {
4088+ if (contentFiberInstance !== null) {
4089+ throw new Error(
4090+ 'A dehydrated Suspense node should not have a content Fiber.',
4091+ );
4092+ }
4093+ }
4094+ } else {
4095+ unmountRemainingChildren();
4096+ }
40434097 removePreviousSuspendedBy(
40444098 instance,
40454099 previousSuspendedBy,
@@ -4049,7 +4103,7 @@ export function attach(
40494103 reconcilingParent = stashedParent;
40504104 previouslyReconciledSibling = stashedPrevious;
40514105 remainingReconcilingChildren = stashedRemaining;
4052- if (instance.suspenseNode !== null ) {
4106+ if (shouldPopSuspenseNode ) {
40534107 reconcilingParentSuspenseNode = stashedSuspenseParent;
40544108 previouslyReconciledSiblingSuspenseNode = stashedSuspensePrevious;
40554109 remainingReconcilingChildrenSuspenseNodes = stashedSuspenseRemaining;
0 commit comments