@@ -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