@@ -16,6 +16,8 @@ import type {
16
16
ReactDebugInfo ,
17
17
} from 'shared/ReactTypes' ;
18
18
import type {
19
+ ContextDependency ,
20
+ Dependencies ,
19
21
Fiber ,
20
22
Dispatcher as DispatcherType ,
21
23
} from 'react-reconciler/src/ReactInternalTypes' ;
@@ -33,6 +35,7 @@ import {
33
35
REACT_MEMO_CACHE_SENTINEL ,
34
36
REACT_CONTEXT_TYPE ,
35
37
} from 'shared/ReactSymbols' ;
38
+ import hasOwnProperty from 'shared/hasOwnProperty' ;
36
39
37
40
type CurrentDispatcherRef = typeof ReactSharedInternals . ReactCurrentDispatcher ;
38
41
@@ -139,6 +142,7 @@ function getPrimitiveStackCache(): Map<string, Array<any>> {
139
142
140
143
let currentFiber : null | Fiber = null ;
141
144
let currentHook : null | Hook = null ;
145
+ let currentContextDependency : null | ContextDependency < mixed > = null ;
142
146
143
147
function nextHook ( ) : null | Hook {
144
148
const hook = currentHook ;
@@ -149,8 +153,29 @@ function nextHook(): null | Hook {
149
153
}
150
154
151
155
function readContext < T > (context: ReactContext< T > ): T {
152
- // For now we don't expose readContext usage in the hooks debugging info.
153
- return context . _currentValue ;
156
+ if ( currentFiber === null ) {
157
+ // Hook inspection without access to the Fiber tree
158
+ // e.g. when warming up the primitive stack cache or during `ReactDebugTools.inspectHooks()`.
159
+ return context . _currentValue ;
160
+ } else {
161
+ if ( currentContextDependency === null ) {
162
+ throw new Error (
163
+ 'Context reads do not line up with context dependencies. This is a bug in React Debug Tools.' ,
164
+ ) ;
165
+ }
166
+
167
+ // For now we don't expose readContext usage in the hooks debugging info.
168
+ const value = hasOwnProperty.call(currentContextDependency, 'memoizedValue')
169
+ ? // $FlowFixMe[incompatible-use] Flow thinks `hasOwnProperty` mutates `currentContextDependency`
170
+ ((currentContextDependency.memoizedValue: any): T)
171
+ : // Before React 18, we did not have `memoizedValue` so we rely on `setupContexts` in those versions.
172
+ // $FlowFixMe[incompatible-use] Flow thinks `hasOwnProperty` mutates `currentContextDependency`
173
+ ((currentContextDependency.context._currentValue: any): T);
174
+ // $FlowFixMe[incompatible-use] Flow thinks `hasOwnProperty` mutates `currentContextDependency`
175
+ currentContextDependency = currentContextDependency.next;
176
+
177
+ return value;
178
+ }
154
179
}
155
180
156
181
const SuspenseException: mixed = new Error (
@@ -218,14 +243,15 @@ function use<T>(usable: Usable<T>): T {
218
243
}
219
244
220
245
function useContext < T > (context: ReactContext< T > ): T {
246
+ const value = readContext ( context ) ;
221
247
hookLog . push ( {
222
248
displayName : context . displayName || null ,
223
249
primitive : 'Context' ,
224
250
stackError : new Error ( ) ,
225
- value : context . _currentValue ,
251
+ value : value ,
226
252
debugInfo : null ,
227
253
} ) ;
228
- return context . _currentValue ;
254
+ return value ;
229
255
}
230
256
231
257
function useState< S > (
@@ -1090,15 +1116,44 @@ export function inspectHooksOfFiber(
1090
1116
currentHook = (fiber.memoizedState: Hook);
1091
1117
currentFiber = fiber;
1092
1118
1119
+ if (hasOwnProperty.call(currentFiber, 'dependencies')) {
1120
+ // $FlowFixMe[incompatible-use]: Flow thinks hasOwnProperty might have nulled `currentFiber`
1121
+ const dependencies = currentFiber . dependencies ;
1122
+ currentContextDependency =
1123
+ dependencies !== null ? dependencies . firstContext : null ;
1124
+ } else if (hasOwnProperty.call(currentFiber, 'dependencies_old')) {
1125
+ const dependencies : Dependencies = ( currentFiber : any ) . dependencies_old ;
1126
+ currentContextDependency =
1127
+ dependencies !== null ? dependencies . firstContext : null ;
1128
+ } else if (hasOwnProperty.call(currentFiber, 'dependencies_new')) {
1129
+ const dependencies : Dependencies = ( currentFiber : any ) . dependencies_new ;
1130
+ currentContextDependency =
1131
+ dependencies !== null ? dependencies . firstContext : null ;
1132
+ } else if (hasOwnProperty.call(currentFiber, 'contextDependencies')) {
1133
+ const contextDependencies = ( currentFiber : any ) . contextDependencies ;
1134
+ currentContextDependency =
1135
+ contextDependencies !== null ? contextDependencies . first : null ;
1136
+ } else {
1137
+ throw new Error (
1138
+ 'Unsupported React version. This is a bug in React Debug Tools.' ,
1139
+ ) ;
1140
+ }
1141
+
1093
1142
const type = fiber.type;
1094
1143
let props = fiber.memoizedProps;
1095
1144
if (type !== fiber.elementType) {
1096
1145
props = resolveDefaultProps ( type , props ) ;
1097
1146
}
1098
1147
1148
+ // Only used for versions of React without memoized context value in context dependencies.
1099
1149
const contextMap = new Map< ReactContext < any > , any> ( ) ;
1100
1150
try {
1101
- setupContexts ( contextMap , fiber ) ;
1151
+ if (
1152
+ currentContextDependency !== null &&
1153
+ ! hasOwnProperty . call ( currentContextDependency , 'memoizedValue' )
1154
+ ) {
1155
+ setupContexts ( contextMap , fiber ) ;
1156
+ }
1102
1157
1103
1158
if (fiber.tag === ForwardRef) {
1104
1159
return inspectHooksOfForwardRef (
@@ -1113,6 +1168,7 @@ export function inspectHooksOfFiber(
1113
1168
} finally {
1114
1169
currentFiber = null ;
1115
1170
currentHook = null ;
1171
+ currentContextDependency = null ;
1116
1172
1117
1173
restoreContexts ( contextMap ) ;
1118
1174
}
0 commit comments