@samitouri / QOS-React-2 / commits / 40c7a7f6ca

[DevTools] Use same Suspense naming heuristics when reconnecting (#34898)

Sebastian "Sebbie" Silbermann committed Oct 18, 2025 at 12:54 UTC 40c7a7f6cac62bd6721328524cc787475ce026eb
6 files changed +64 -16
packages/react-devtools-shared/src/__tests__/store-test.js
+57
@@ -3243,4 +3243,61 @@ describe('Store', () => {
3243 <Suspense name="inner-suspense" rects={[{x:1,y:2,width:15,height:1}]}>
3244 `);
3245 });
3246 +
3247 + // @reactVersion >= 19.0
3248 + it('guesses a Suspense name based on the owner', async () => {
3249 + let resolve;
3250 + const promise = new Promise(_resolve => {
3251 + resolve = _resolve;
3252 + });
3253 + function Inner() {
3254 + return (
3255 + <React.Suspense fallback={<p>Loading inner</p>}>
3256 + <p>{promise}</p>
3257 + </React.Suspense>
3258 + );
3259 + }
3260 +
3261 + function Outer({children}) {
3262 + return (
3263 + <React.Suspense fallback={<p>Loading outer</p>}>
3264 + <p>{promise}</p>
3265 + {children}
3266 + </React.Suspense>
3267 + );
3268 + }
3269 +
3270 + await actAsync(() => {
3271 + render(
3272 + <Outer>
3273 + <Inner />
3274 + </Outer>,
3275 + );
3276 + });
3277 +
3278 + expect(store).toMatchInlineSnapshot(`
3279 + [root]
3280 + ▾ <Outer>
3281 + <Suspense>
3282 + [suspense-root] rects={[{x:1,y:2,width:13,height:1}]}
3283 + <Suspense name="Outer" rects={null}>
3284 + `);
3285 +
3286 + console.log('...........................');
3287 +
3288 + await actAsync(() => {
3289 + resolve('loaded');
3290 + });
3291 +
3292 + expect(store).toMatchInlineSnapshot(`
3293 + [root]
3294 + ▾ <Outer>
3295 + ▾ <Suspense>
3296 + ▾ <Inner>
3297 + <Suspense>
3298 + [suspense-root] rects={[{x:1,y:2,width:6,height:1}, {x:1,y:2,width:6,height:1}]}
3299 + <Suspense name="Outer" rects={[{x:1,y:2,width:6,height:1}, {x:1,y:2,width:6,height:1}]}>
3300 + <Suspense name="Inner" rects={[{x:1,y:2,width:6,height:1}]}>
3301 + `);
3302 + });
3303 });
packages/react-devtools-shared/src/backend/fiber/renderer.js
+1 -1
@@ -2664,7 +2664,7 @@ export function attach(
2664
2665 const fiber = fiberInstance.data;
2666 const props = fiber.memoizedProps;
2667 - // TODO: Compute a fallback name based on Owner, key etc.
2667 + // The frontend will guess a name based on heuristics (e.g. owner) if no explicit name is given.
2668 const name =
2669 fiber.tag !== SuspenseComponent || props === null
2670 ? null
packages/react-devtools-shared/src/devtools/store.js
+3 -8
@@ -1646,10 +1646,6 @@ export default class Store extends EventEmitter<{
1646 parentSuspense.children.push(id);
1647 }
1648
1649 - if (name === null) {
1650 - name = 'Unknown';
1651 - }
1652 -
1649 this._idToSuspense.set(id, {
1650 id,
1651 parentID,
@@ -2170,13 +2166,12 @@ export default class Store extends EventEmitter<{
2166 throw error;
2167 }
2168
2173 - _guessSuspenseName(element: Element): string {
2169 + _guessSuspenseName(element: Element): string | null {
2170 const owner = this._idToElement.get(element.ownerID);
2175 - let name = 'Unknown';
2171 if (owner !== undefined && owner.displayName !== null) {
2177 - name = owner.displayName;
2172 + return owner.displayName;
2173 }
2174
2180 - return name;
2175 + return null;
2176 }
2177 }
packages/react-devtools-shared/src/devtools/utils.js
+1 -5
@@ -63,11 +63,7 @@ function printRects(rects: SuspenseNode['rects']): string {
63 }
64
65 function printSuspense(suspense: SuspenseNode): string {
66 - let name = '';
67 - if (suspense.name !== null) {
68 - name = ` name="${suspense.name}"`;
69 - }
70 -
66 + const name = ` name="${suspense.name || 'Unknown'}"`;
67 const printedRects = printRects(suspense.rects);
68
69 return `<Suspense${name}${printedRects}>`;
packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseBreadcrumbs.js
+1 -1
@@ -72,7 +72,7 @@ export default function SuspenseBreadcrumbs(): React$Node {
72 className={styles.SuspenseBreadcrumbsButton}
73 onClick={handleClick.bind(null, id)}
74 type="button">
75 - {node === null ? 'Unknown' : node.name}
75 + {node === null ? 'Unknown' : node.name || 'Unknown'}
76 </button>
77 </li>
78 );
packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js
+1 -1
@@ -196,7 +196,7 @@ function SuspenseRects({
196 onPointerOver={handlePointerOver}
197 onPointerLeave={handlePointerLeave}
198 // Reach-UI tooltip will go out of bounds of parent scroll container.
199 - title={suspense.name}
199 + title={suspense.name || 'Unknown'}
200 />
201 );
202 })}