[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
}