@samitouri / QOS-React-1 / commits / 62a634b972

[DebugTools] Use thenables from the _debugThenableState if available (#34161)

In the case where a Promise is not cached, then the thenable state might contain an older version. This version is the one that was actually observed by the committed render, so that's the version we'll want to inspect. We used to not store the thenable state but now we have it on `_debugThenableState` in DEV. <img width="593" height="359" alt="Screenshot 2025-08-10 at 8 26 04 PM" src="https://github.com/user-attachments/assets/51ee53f3-a31a-4e3f-a4cf-bb20b6efe0cb" />

Sebastian Markbåge committed Aug 11, 2025 at 11:46 UTC 62a634b9722aa77d7ddb1fe8017db7d3a0925389
1 file changed +21 -1
packages/react-debug-tools/src/ReactDebugHooks.js
+21 -1
@@ -147,6 +147,8 @@ function getPrimitiveStackCache(): Map<string, Array<any>> {
147 let currentFiber: null | Fiber = null;
148 let currentHook: null | Hook = null;
149 let currentContextDependency: null | ContextDependency<mixed> = null;
150 +let currentThenableIndex: number = 0;
151 +let currentThenableState: null | Array<Thenable<mixed>> = null;
152
153 function nextHook(): null | Hook {
154 const hook = currentHook;
@@ -201,7 +203,15 @@ function use<T>(usable: Usable<T>): T {
203 if (usable !== null && typeof usable === 'object') {
204 // $FlowFixMe[method-unbinding]
205 if (typeof usable.then === 'function') {
204 - const thenable: Thenable<any> = (usable: any);
206 + const thenable: Thenable<any> =
207 + // If we have thenable state, then the actually used thenable will be the one
208 + // stashed in it. It's possible for uncached Promises to be new each render
209 + // and in that case the one we're inspecting is the in the thenable state.
210 + currentThenableState !== null &&
211 + currentThenableIndex < currentThenableState.length
212 + ? currentThenableState[currentThenableIndex++]
213 + : (usable: any);
214 +
215 switch (thenable.status) {
216 case 'fulfilled': {
217 const fulfilledValue: T = thenable.value;
@@ -1285,6 +1295,14 @@ export function inspectHooksOfFiber(
1295 // current state from them.
1296 currentHook = (fiber.memoizedState: Hook);
1297 currentFiber = fiber;
1298 + const thenableState =
1299 + fiber.dependencies && fiber.dependencies._debugThenableState;
1300 + // In DEV the thenableState is an inner object.
1301 + const usedThenables: any = thenableState
1302 + ? thenableState.thenables || thenableState
1303 + : null;
1304 + currentThenableState = Array.isArray(usedThenables) ? usedThenables : null;
1305 + currentThenableIndex = 0;
1306
1307 if (hasOwnProperty.call(currentFiber, 'dependencies')) {
1308 // $FlowFixMe[incompatible-use]: Flow thinks hasOwnProperty might have nulled `currentFiber`
@@ -1339,6 +1357,8 @@ export function inspectHooksOfFiber(
1357 currentFiber = null;
1358 currentHook = null;
1359 currentContextDependency = null;
1360 + currentThenableState = null;
1361 + currentThenableIndex = 0;
1362
1363 restoreContexts(contextMap);
1364 }