[DevTools] Record Suspense node for roots in legacy renderers (#34516)
Sebastian "Sebbie" Silbermann committed
Sep 18, 2025 at 18:50 UTC
03a96c75db12eeac36b4d5d88c13e3a12d46efe1
2 files changed
+23
-3
packages/react-devtools-shared/src/backend/legacy/renderer.js
+21
-1
@@ -34,6 +34,8 @@ import {
34
TREE_OPERATION_ADD,
35
TREE_OPERATION_REMOVE,
36
TREE_OPERATION_REORDER_CHILDREN,
37
+ SUSPENSE_TREE_OPERATION_ADD,
38
+ SUSPENSE_TREE_OPERATION_REMOVE,
39
UNKNOWN_SUSPENDERS_NONE,
40
} from '../../constants';
41
import {decorateMany, forceUpdate, restoreMany} from './utils';
@@ -411,6 +413,13 @@ export function attach(
413
pushOperation(0); // StrictMode supported?
414
pushOperation(hasOwnerMetadata ? 1 : 0);
415
pushOperation(supportsTogglingSuspense ? 1 : 0);
416
+
417
+ pushOperation(SUSPENSE_TREE_OPERATION_ADD);
418
+ pushOperation(id);
419
+ pushOperation(parentID);
420
+ pushOperation(getStringID(null)); // name
421
+ // TODO: Measure rect of root
422
+ pushOperation(-1);
423
} else {
424
const type = getElementType(internalInstance);
425
const {displayName, key} = getData(internalInstance);
@@ -449,7 +458,12 @@ export function attach(
458
}
459
460
function recordUnmount(internalInstance: InternalInstance, id: number) {
452
- pendingUnmountedIDs.push(id);
461
+ const isRoot = parentIDStack.length === 0;
462
+ if (isRoot) {
463
+ pendingUnmountedRootID = id;
464
+ } else {
465
+ pendingUnmountedIDs.push(id);
466
+ }
467
idToInternalInstanceMap.delete(id);
468
}
469
@@ -519,6 +533,8 @@ export function attach(
533
// All unmounts are batched in a single message.
534
// [TREE_OPERATION_REMOVE, removedIDLength, ...ids]
535
(numUnmountIDs > 0 ? 2 + numUnmountIDs : 0) +
536
+ // [SUSPENSE_TREE_OPERATION_REMOVE, 1, pendingUnmountedRootID]
537
+ (pendingUnmountedRootID === null ? 0 : 3) +
538
// Mount operations
539
pendingOperations.length,
540
);
@@ -555,6 +571,10 @@ export function attach(
571
if (pendingUnmountedRootID !== null) {
572
operations[i] = pendingUnmountedRootID;
573
i++;
574
+
575
+ operations[i++] = SUSPENSE_TREE_OPERATION_REMOVE;
576
+ operations[i++] = 1;
577
+ operations[i++] = pendingUnmountedRootID;
578
}
579
}
580
packages/react-devtools-shared/src/devtools/store.js
+2
-2
@@ -895,11 +895,11 @@ export default class Store extends EventEmitter<{
895
if (root === null) {
896
return [];
897
}
898
- if (!this.supportsTogglingSuspense(root.id)) {
898
+ if (!this.supportsTogglingSuspense(rootID)) {
899
return [];
900
}
901
const list: SuspenseNode['id'][] = [];
902
- const suspense = this.getSuspenseByID(root.id);
902
+ const suspense = this.getSuspenseByID(rootID);
903
if (suspense !== null) {
904
const stack = [suspense];
905
while (stack.length > 0) {