Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion packages/react-devtools-shared/src/backend/legacy/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import {
TREE_OPERATION_ADD,
TREE_OPERATION_REMOVE,
TREE_OPERATION_REORDER_CHILDREN,
SUSPENSE_TREE_OPERATION_ADD,
SUSPENSE_TREE_OPERATION_REMOVE,
UNKNOWN_SUSPENDERS_NONE,
} from '../../constants';
import {decorateMany, forceUpdate, restoreMany} from './utils';
Expand Down Expand Up @@ -411,6 +413,13 @@ export function attach(
pushOperation(0); // StrictMode supported?
pushOperation(hasOwnerMetadata ? 1 : 0);
pushOperation(supportsTogglingSuspense ? 1 : 0);

pushOperation(SUSPENSE_TREE_OPERATION_ADD);
pushOperation(id);
pushOperation(parentID);
pushOperation(getStringID(null)); // name
// TODO: Measure rect of root
pushOperation(-1);
} else {
const type = getElementType(internalInstance);
const {displayName, key} = getData(internalInstance);
Expand Down Expand Up @@ -449,7 +458,12 @@ export function attach(
}

function recordUnmount(internalInstance: InternalInstance, id: number) {
pendingUnmountedIDs.push(id);
const isRoot = parentIDStack.length === 0;
if (isRoot) {
pendingUnmountedRootID = id;
Copy link
Collaborator Author

@eps1lon eps1lon Sep 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pendingUnmountedRootID was never actually written to. The root was just unmounted by virtue of being included in the normal unmount operation. There wasn't anything special to be done with the root until now.

A single unmounted root ID is sound since we always flush operations after unmounting a component.

} else {
pendingUnmountedIDs.push(id);
}
idToInternalInstanceMap.delete(id);
}

Expand Down Expand Up @@ -519,6 +533,8 @@ export function attach(
// All unmounts are batched in a single message.
// [TREE_OPERATION_REMOVE, removedIDLength, ...ids]
(numUnmountIDs > 0 ? 2 + numUnmountIDs : 0) +
// [SUSPENSE_TREE_OPERATION_REMOVE, 1, pendingUnmountedRootID]
(pendingUnmountedRootID === null ? 0 : 3) +
// Mount operations
pendingOperations.length,
);
Expand Down Expand Up @@ -555,6 +571,10 @@ export function attach(
if (pendingUnmountedRootID !== null) {
operations[i] = pendingUnmountedRootID;
i++;

operations[i++] = SUSPENSE_TREE_OPERATION_REMOVE;
operations[i++] = 1;
operations[i++] = pendingUnmountedRootID;
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/react-devtools-shared/src/devtools/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -895,11 +895,11 @@ export default class Store extends EventEmitter<{
if (root === null) {
return [];
}
if (!this.supportsTogglingSuspense(root.id)) {
if (!this.supportsTogglingSuspense(rootID)) {
return [];
}
const list: SuspenseNode['id'][] = [];
const suspense = this.getSuspenseByID(root.id);
const suspense = this.getSuspenseByID(rootID);
if (suspense !== null) {
const stack = [suspense];
while (stack.length > 0) {
Expand Down
Loading