[Fiber] Stash ThenableState on the Dependencies Object for Use By DevTools (#30866)
This lets us track what a Component might suspend on from DevTools. We could already collect this by replaying a component's Hooks but that would be expensive to collect from a whole tree. The thenables themselves might contain useful information but mainly we'd want access to the `_debugInfo` on the thenables which might contain additional information from the server. https://github.com/facebook/react/blob/19bd26beb689e554fceb0b929dc5199be8cba594/packages/shared/ReactTypes.js#L114 In a follow up we should really do something similar in Flight to transfer `use()` on the debugInfo of that Server Component.
Sebastian Markbåge committed
Sep 3, 2024 at 16:04 UTC
8d68da3f7396064614f34b84881fe8833b6039ac
4 files changed
+54
-17
packages/react-reconciler/src/ReactFiber.js
+20
-8
@@ -404,10 +404,16 @@ export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
404
workInProgress.dependencies =
405
currentDependencies === null
406
? null
407
- : {
408
- lanes: currentDependencies.lanes,
409
- firstContext: currentDependencies.firstContext,
410
- };
407
+ : __DEV__
408
+ ? {
409
+ lanes: currentDependencies.lanes,
410
+ firstContext: currentDependencies.firstContext,
411
+ _debugThenableState: currentDependencies._debugThenableState,
412
+ }
413
+ : {
414
+ lanes: currentDependencies.lanes,
415
+ firstContext: currentDependencies.firstContext,
416
+ };
417
418
// These will be overridden during the parent's reconciliation
419
workInProgress.sibling = current.sibling;
@@ -503,10 +509,16 @@ export function resetWorkInProgress(
509
workInProgress.dependencies =
510
currentDependencies === null
511
? null
506
- : {
507
- lanes: currentDependencies.lanes,
508
- firstContext: currentDependencies.firstContext,
509
- };
512
+ : __DEV__
513
+ ? {
514
+ lanes: currentDependencies.lanes,
515
+ firstContext: currentDependencies.firstContext,
516
+ _debugThenableState: currentDependencies._debugThenableState,
517
+ }
518
+ : {
519
+ lanes: currentDependencies.lanes,
520
+ firstContext: currentDependencies.firstContext,
521
+ };
522
523
if (enableProfilerTimer) {
524
// Note: We don't reset the actualTime counts. It's useful to accumulate
packages/react-reconciler/src/ReactFiberHooks.js
+12
@@ -637,6 +637,18 @@ function finishRenderingHooks<Props, SecondArg>(
637
): void {
638
if (__DEV__) {
639
workInProgress._debugHookTypes = hookTypesDev;
640
+ // Stash the thenable state for use by DevTools.
641
+ if (workInProgress.dependencies === null) {
642
+ if (thenableState !== null) {
643
+ workInProgress.dependencies = {
644
+ lanes: NoLanes,
645
+ firstContext: null,
646
+ _debugThenableState: thenableState,
647
+ };
648
+ }
649
+ } else {
650
+ workInProgress.dependencies._debugThenableState = thenableState;
651
+ }
652
}
653
654
// We can assume the previous dispatcher is always this one, since we set it
packages/react-reconciler/src/ReactFiberNewContext.js
+20
-8
@@ -825,10 +825,16 @@ function readContextForConsumer_withSelect<C>(
825
826
// This is the first dependency for this component. Create a new list.
827
lastContextDependency = contextItem;
828
- consumer.dependencies = {
829
- lanes: NoLanes,
830
- firstContext: contextItem,
831
- };
828
+ consumer.dependencies = __DEV__
829
+ ? {
830
+ lanes: NoLanes,
831
+ firstContext: contextItem,
832
+ _debugThenableState: null,
833
+ }
834
+ : {
835
+ lanes: NoLanes,
836
+ firstContext: contextItem,
837
+ };
838
if (enableLazyContextPropagation) {
839
consumer.flags |= NeedsPropagation;
840
}
@@ -869,10 +875,16 @@ function readContextForConsumer<C>(
875
876
// This is the first dependency for this component. Create a new list.
877
lastContextDependency = contextItem;
872
- consumer.dependencies = {
873
- lanes: NoLanes,
874
- firstContext: contextItem,
875
- };
878
+ consumer.dependencies = __DEV__
879
+ ? {
880
+ lanes: NoLanes,
881
+ firstContext: contextItem,
882
+ _debugThenableState: null,
883
+ }
884
+ : {
885
+ lanes: NoLanes,
886
+ firstContext: contextItem,
887
+ };
888
if (enableLazyContextPropagation) {
889
consumer.flags |= NeedsPropagation;
890
}
packages/react-reconciler/src/ReactInternalTypes.js
+2
-1
@@ -37,6 +37,7 @@ import type {
37
} from './ReactFiberTracingMarkerComponent';
38
import type {ConcurrentUpdate} from './ReactFiberConcurrentUpdates';
39
import type {ComponentStackNode} from 'react-server/src/ReactFizzComponentStack';
40
+import type {ThenableState} from './ReactFiberThenable';
41
42
// Unwind Circular: moved from ReactFiberHooks.old
43
export type HookType =
@@ -81,7 +82,7 @@ export type Dependencies = {
82
| ContextDependency<mixed>
83
| ContextDependencyWithSelect<mixed>
84
| null,
84
- ...
85
+ _debugThenableState?: null | ThenableState, // DEV-only
86
};
87
88
export type MemoCache = {