Skip to content

Commit ab5238d

Browse files
authored
[DevTools] Show name prop of Suspense / Activity in the Components Tree view (#34135)
The name prop will be used in the Suspense tab to help identity a boundary. Activity will also allow names. A custom component can be identified by the name of the component but built-ins doesn't have that. This PR adds it to the Components Tree View as well since otherwise you only have the key to go on. Normally we don't add all the props to avoid making this view too noisy but this is an exception along with key to help identify a boundary quickly in the tree. Unlike the SuspenseNode store, this wouldn't ever have a name inferred by owner since that kind of context already exists in this view. <img width="600" height="161" alt="Screenshot 2025-08-08 at 1 20 36 PM" src="https://github.com/user-attachments/assets/fe50d624-887a-4b9d-9186-75f131f83195" /> I also made both the key and name prop searchable. <img width="608" height="206" alt="Screenshot 2025-08-08 at 1 32 27 PM" src="https://github.com/user-attachments/assets/d3502d9c-7614-45fc-b973-57f06dd9cddc" />
1 parent 7a934a1 commit ab5238d

File tree

9 files changed

+56
-2
lines changed

9 files changed

+56
-2
lines changed

packages/react-devtools-shared/src/backend/fiber/renderer.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2369,13 +2369,23 @@ export function attach(
23692369
const keyString = key === null ? null : String(key);
23702370
const keyStringID = getStringID(keyString);
23712371
2372+
const nameProp =
2373+
fiber.tag === SuspenseComponent
2374+
? fiber.memoizedProps.name
2375+
: fiber.tag === ActivityComponent
2376+
? fiber.memoizedProps.name
2377+
: null;
2378+
const namePropString = nameProp == null ? null : String(nameProp);
2379+
const namePropStringID = getStringID(namePropString);
2380+
23722381
pushOperation(TREE_OPERATION_ADD);
23732382
pushOperation(id);
23742383
pushOperation(elementType);
23752384
pushOperation(parentID);
23762385
pushOperation(ownerID);
23772386
pushOperation(displayNameStringID);
23782387
pushOperation(keyStringID);
2388+
pushOperation(namePropStringID);
23792389
23802390
// If this subtree has a new mode, let the frontend know.
23812391
if ((fiber.mode & StrictModeBits) !== 0) {
@@ -2478,6 +2488,7 @@ export function attach(
24782488
// in such a way as to bypass the default stringification of the "key" property.
24792489
const keyString = key === null ? null : String(key);
24802490
const keyStringID = getStringID(keyString);
2491+
const namePropStringID = getStringID(null);
24812492
24822493
const id = instance.id;
24832494
@@ -2488,6 +2499,7 @@ export function attach(
24882499
pushOperation(ownerID);
24892500
pushOperation(displayNameStringID);
24902501
pushOperation(keyStringID);
2502+
pushOperation(namePropStringID);
24912503
24922504
const componentLogsEntry =
24932505
componentInfoToComponentLogsMap.get(componentInfo);

packages/react-devtools-shared/src/backend/legacy/renderer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ export function attach(
426426
pushOperation(ownerID);
427427
pushOperation(displayNameStringID);
428428
pushOperation(keyStringID);
429+
pushOperation(getStringID(null)); // name prop
429430
}
430431
}
431432

packages/react-devtools-shared/src/devtools/store.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,6 +1116,7 @@ export default class Store extends EventEmitter<{
11161116
isCollapsed: false, // Never collapse roots; it would hide the entire tree.
11171117
isStrictModeNonCompliant,
11181118
key: null,
1119+
nameProp: null,
11191120
ownerID: 0,
11201121
parentID: 0,
11211122
type,
@@ -1139,6 +1140,10 @@ export default class Store extends EventEmitter<{
11391140
const key = stringTable[keyStringID];
11401141
i++;
11411142

1143+
const namePropStringID = operations[i];
1144+
const nameProp = stringTable[namePropStringID];
1145+
i++;
1146+
11421147
if (__DEBUG__) {
11431148
debug(
11441149
'Add',
@@ -1180,6 +1185,7 @@ export default class Store extends EventEmitter<{
11801185
isCollapsed: this._collapseNodesByDefault,
11811186
isStrictModeNonCompliant: parentElement.isStrictModeNonCompliant,
11821187
key,
1188+
nameProp,
11831189
ownerID,
11841190
parentID,
11851191
type,

packages/react-devtools-shared/src/devtools/views/Components/Element.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ export default function Element({data, index, style}: Props): React.Node {
119119
hocDisplayNames,
120120
isStrictModeNonCompliant,
121121
key,
122+
nameProp,
122123
compiledWithForget,
123124
} = element;
124125
const {
@@ -179,7 +180,24 @@ export default function Element({data, index, style}: Props): React.Node {
179180
className={styles.KeyValue}
180181
title={key}
181182
onDoubleClick={handleKeyDoubleClick}>
182-
<pre>{key}</pre>
183+
<pre>
184+
<IndexableDisplayName displayName={key} id={id} />
185+
</pre>
186+
</span>
187+
"
188+
</Fragment>
189+
)}
190+
191+
{nameProp && (
192+
<Fragment>
193+
&nbsp;<span className={styles.KeyName}>name</span>="
194+
<span
195+
className={styles.KeyValue}
196+
title={nameProp}
197+
onDoubleClick={handleKeyDoubleClick}>
198+
<pre>
199+
<IndexableDisplayName displayName={nameProp} id={id} />
200+
</pre>
183201
</span>
184202
"
185203
</Fragment>

packages/react-devtools-shared/src/devtools/views/Components/TreeContext.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -995,7 +995,14 @@ function recursivelySearchTree(
995995
return;
996996
}
997997

998-
const {children, displayName, hocDisplayNames, compiledWithForget} = element;
998+
const {
999+
children,
1000+
displayName,
1001+
hocDisplayNames,
1002+
compiledWithForget,
1003+
key,
1004+
nameProp,
1005+
} = element;
9991006
if (displayName != null && regExp.test(displayName) === true) {
10001007
searchResults.push(elementID);
10011008
} else if (
@@ -1006,6 +1013,10 @@ function recursivelySearchTree(
10061013
searchResults.push(elementID);
10071014
} else if (compiledWithForget && regExp.test('Forget')) {
10081015
searchResults.push(elementID);
1016+
} else if (typeof key === 'string' && regExp.test(key)) {
1017+
searchResults.push(elementID);
1018+
} else if (typeof nameProp === 'string' && regExp.test(nameProp)) {
1019+
searchResults.push(elementID);
10091020
}
10101021

10111022
children.forEach(childID =>

packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,9 @@ function updateTree(
239239
const key = stringTable[keyStringID];
240240
i++;
241241

242+
// skip name prop
243+
i++;
244+
242245
if (__DEBUG__) {
243246
debug(
244247
'Add',

packages/react-devtools-shared/src/frontend/types.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ export type Element = {
157157
type: ElementType,
158158
displayName: string | null,
159159
key: number | string | null,
160+
nameProp: null | string,
160161

161162
hocDisplayNames: null | Array<string>,
162163

packages/react-devtools-shared/src/utils.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ export function printOperationsArray(operations: Array<number>) {
271271
i++;
272272

273273
i++; // key
274+
i++; // name
274275

275276
logs.push(
276277
`Add node ${id} (${displayName || 'null'}) as child of ${parentID}`,

packages/shared/ReactTypes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ export type ViewTransitionProps = {
298298
export type ActivityProps = {
299299
mode?: 'hidden' | 'visible' | null | void,
300300
children?: ReactNodeList,
301+
name?: string,
301302
};
302303

303304
export type SuspenseProps = {

0 commit comments

Comments
 (0)