[DevTools] Restore reconciling Suspense stack after fallback was reconciled (#34168)
Sebastian "Sebbie" Silbermann committed
Aug 11, 2025 at 17:12 UTC
72965f361547da79fcd4310ae13e22d6abb274a6
1 file changed
+37
-33
packages/react-devtools-shared/src/backend/fiber/renderer.js
+37
-33
@@ -4162,7 +4162,7 @@ export function attach(
4162
const stashedSuspenseParent = reconcilingParentSuspenseNode;
4163
const stashedSuspensePrevious = previouslyReconciledSiblingSuspenseNode;
4164
const stashedSuspenseRemaining = remainingReconcilingChildrenSuspenseNodes;
4165
- let shouldPopSuspenseNode = false;
4165
+ let shouldMeasureSuspenseNode = false;
4166
let previousSuspendedBy = null;
4167
if (fiberInstance !== null) {
4168
previousSuspendedBy = fiberInstance.suspendedBy;
@@ -4192,7 +4192,7 @@ export function attach(
4192
previouslyReconciledSiblingSuspenseNode = null;
4193
remainingReconcilingChildrenSuspenseNodes = suspenseNode.firstChild;
4194
suspenseNode.firstChild = null;
4195
- shouldPopSuspenseNode = true;
4195
+ shouldMeasureSuspenseNode = true;
4196
}
4197
}
4198
try {
@@ -4379,38 +4379,40 @@ export function attach(
4379
0,
4380
);
4381
4382
- // Next, we'll pop back out of the SuspenseNode that we added above and now we'll
4383
- // reconcile the fallback, reconciling anything by inserting into the parent SuspenseNode.
4384
- // Since the fallback conceptually blocks the parent.
4385
- reconcilingParentSuspenseNode = stashedSuspenseParent;
4386
- previouslyReconciledSiblingSuspenseNode = stashedSuspensePrevious;
4387
- remainingReconcilingChildrenSuspenseNodes = stashedSuspenseRemaining;
4388
- shouldPopSuspenseNode = false;
4382
+ shouldMeasureSuspenseNode = false;
4383
if (nextFallbackFiber !== null) {
4390
- updateFlags |= updateVirtualChildrenRecursively(
4391
- nextFallbackFiber,
4392
- null,
4393
- prevFallbackFiber,
4394
- traceNearestHostComponentUpdate,
4395
- 0,
4396
- );
4397
- } else if (
4398
- nextFiber.memoizedState === null &&
4399
- fiberInstance.suspenseNode !== null
4400
- ) {
4401
- if (!isInDisconnectedSubtree) {
4402
- // Measure this Suspense node in case it changed. We don't update the rect while
4403
- // we're inside a disconnected subtree nor if we are the Suspense boundary that
4404
- // is suspended. This lets us keep the rectangle of the displayed content while
4405
- // we're suspended to visualize the resulting state.
4406
- const suspenseNode = fiberInstance.suspenseNode;
4407
- const prevRects = suspenseNode.rects;
4408
- const nextRects = measureInstance(fiberInstance);
4409
- if (!areEqualRects(prevRects, nextRects)) {
4410
- suspenseNode.rects = nextRects;
4411
- recordSuspenseResize(suspenseNode);
4412
- }
4384
+ const fallbackStashedSuspenseParent = reconcilingParentSuspenseNode;
4385
+ const fallbackStashedSuspensePrevious =
4386
+ previouslyReconciledSiblingSuspenseNode;
4387
+ const fallbackStashedSuspenseRemaining =
4388
+ remainingReconcilingChildrenSuspenseNodes;
4389
+ // Next, we'll pop back out of the SuspenseNode that we added above and now we'll
4390
+ // reconcile the fallback, reconciling anything by inserting into the parent SuspenseNode.
4391
+ // Since the fallback conceptually blocks the parent.
4392
+ reconcilingParentSuspenseNode = stashedSuspenseParent;
4393
+ previouslyReconciledSiblingSuspenseNode = stashedSuspensePrevious;
4394
+ remainingReconcilingChildrenSuspenseNodes = stashedSuspenseRemaining;
4395
+ try {
4396
+ updateFlags |= updateVirtualChildrenRecursively(
4397
+ nextFallbackFiber,
4398
+ null,
4399
+ prevFallbackFiber,
4400
+ traceNearestHostComponentUpdate,
4401
+ 0,
4402
+ );
4403
+ } finally {
4404
+ reconcilingParentSuspenseNode = fallbackStashedSuspenseParent;
4405
+ previouslyReconciledSiblingSuspenseNode =
4406
+ fallbackStashedSuspensePrevious;
4407
+ remainingReconcilingChildrenSuspenseNodes =
4408
+ fallbackStashedSuspenseRemaining;
4409
}
4410
+ } else if (nextFiber.memoizedState === null) {
4411
+ // Measure this Suspense node in case it changed. We don't update the rect while
4412
+ // we're inside a disconnected subtree nor if we are the Suspense boundary that
4413
+ // is suspended. This lets us keep the rectangle of the displayed content while
4414
+ // we're suspended to visualize the resulting state.
4415
+ shouldMeasureSuspenseNode = !isInDisconnectedSubtree;
4416
}
4417
} else {
4418
// Common case: Primary -> Primary.
@@ -4519,7 +4521,7 @@ export function attach(
4521
reconcilingParent = stashedParent;
4522
previouslyReconciledSibling = stashedPrevious;
4523
remainingReconcilingChildren = stashedRemaining;
4522
- if (shouldPopSuspenseNode) {
4524
+ if (shouldMeasureSuspenseNode) {
4525
if (
4526
!isInDisconnectedSubtree &&
4527
reconcilingParentSuspenseNode !== null
@@ -4535,6 +4537,8 @@ export function attach(
4537
recordSuspenseResize(suspenseNode);
4538
}
4539
}
4540
+ }
4541
+ if (fiberInstance.suspenseNode !== null) {
4542
reconcilingParentSuspenseNode = stashedSuspenseParent;
4543
previouslyReconciledSiblingSuspenseNode = stashedSuspensePrevious;
4544
remainingReconcilingChildrenSuspenseNodes = stashedSuspenseRemaining;