@samitouri / QOS-React-2 / commits / d587434c35

[DevTools] Pick up suspended by info from use() (#34148)

Similar to #34144 but for `use()`. `use()` dependencies don't get added to the `fiber._debugInfo` set because that just models the things blocking the children, and not the Fiber component itself. This picks up any debug info from the thenable state that we stashed onto `_debugThenableState` so that we know it used `use()`. <img width="593" height="425" alt="Screenshot 2025-08-09 at 4 03 40 PM" src="https://github.com/user-attachments/assets/c7e06884-4efd-47fa-a76b-132935db6ddc" /> Without #34146 this doesn't pick up uninstrumented promises but after it, it'll pick those up as well. An instrumented promise that doesn't have anything in its debug info is not picked up. For example, if it didn't depend on any I/O on the server. This doesn't yet pick up the stack trace of the `use()` call. That information is in the Hooks information but needs a follow up to extract it.

Sebastian Markbåge committed Aug 11, 2025 at 12:10 UTC d587434c350a9ba317285ce7b535add47ab3c205
2 files changed +37 -1
packages/react-devtools-shared/src/__tests__/inspectedElement-test.js
+1 -1
@@ -791,7 +791,7 @@ describe('InspectedElement', () => {
791 "react_lazy": {
792 "_payload": Dehydrated {
793 "preview_short": {…},
794 - "preview_long": {_result: () => {}, _status: -1},
794 + "preview_long": {_ioInfo: {…}, _result: () => {}, _status: -1},
795 },
796 },
797 "regexp": Dehydrated {
packages/react-devtools-shared/src/backend/fiber/renderer.js
+36
@@ -8,6 +8,7 @@
8 */
9
10 import type {
11 + Thenable,
12 ReactComponentInfo,
13 ReactDebugInfo,
14 ReactAsyncInfo,
@@ -3181,6 +3182,39 @@ export function attach(
3182 }
3183 }
3184
3185 + function trackDebugInfoFromUsedThenables(fiber: Fiber): void {
3186 + // If a Fiber called use() in DEV mode then we may have collected _debugThenableState on
3187 + // the dependencies. If so, then this will contain the thenables passed to use().
3188 + // These won't have their debug info picked up by fiber._debugInfo since that just
3189 + // contains things suspending the children. We have to collect use() separately.
3190 + const dependencies = fiber.dependencies;
3191 + if (dependencies == null) {
3192 + return;
3193 + }
3194 + const thenableState = dependencies._debugThenableState;
3195 + if (thenableState == null) {
3196 + return;
3197 + }
3198 + // In DEV the thenableState is an inner object.
3199 + const usedThenables: any = thenableState.thenables || thenableState;
3200 + if (!Array.isArray(usedThenables)) {
3201 + return;
3202 + }
3203 + for (let i = 0; i < usedThenables.length; i++) {
3204 + const thenable: Thenable<mixed> = usedThenables[i];
3205 + const debugInfo = thenable._debugInfo;
3206 + if (debugInfo) {
3207 + for (let j = 0; j < debugInfo.length; j++) {
3208 + const debugEntry = debugInfo[i];
3209 + if (debugEntry.awaited) {
3210 + const asyncInfo: ReactAsyncInfo = (debugEntry: any);
3211 + insertSuspendedBy(asyncInfo);
3212 + }
3213 + }
3214 + }
3215 + }
3216 + }
3217 +
3218 function mountVirtualChildrenRecursively(
3219 firstChild: Fiber,
3220 lastChild: null | Fiber, // non-inclusive
@@ -3400,6 +3434,7 @@ export function attach(
3434 }
3435
3436 trackDebugInfoFromLazyType(fiber);
3437 + trackDebugInfoFromUsedThenables(fiber);
3438
3439 if (fiber.tag === HostHoistable) {
3440 const nearestInstance = reconcilingParent;
@@ -4231,6 +4266,7 @@ export function attach(
4266 }
4267 try {
4268 trackDebugInfoFromLazyType(nextFiber);
4269 + trackDebugInfoFromUsedThenables(nextFiber);
4270
4271 if (
4272 nextFiber.tag === HostHoistable &&