devtools: Use context displayName for context hook name (#25954)
Sebastian Silbermann committed
Feb 24, 2024 at 11:54 UTC
aed00dacfb79d17c53218404c52b1c7aa59c4a89
2 files changed
+41
-2
packages/react-debug-tools/src/ReactDebugHooks.js
+24
-2
@@ -39,6 +39,7 @@ type CurrentDispatcherRef = typeof ReactSharedInternals.ReactCurrentDispatcher;
39
// Used to track hooks called during a render
40
41
type HookLogEntry = {
42
+ displayName: string | null,
43
primitive: string,
44
stackError: Error,
45
value: mixed,
@@ -171,6 +172,7 @@ function use<T>(usable: Usable<T>): T {
172
case 'fulfilled': {
173
const fulfilledValue: T = thenable.value;
174
hookLog.push({
175
+ displayName: null,
176
primitive: 'Promise',
177
stackError: new Error(),
178
value: fulfilledValue,
@@ -187,6 +189,7 @@ function use<T>(usable: Usable<T>): T {
189
// If this was an uncached Promise we have to abandon this attempt
190
// but we can still emit anything up until this point.
191
hookLog.push({
192
+ displayName: null,
193
primitive: 'Unresolved',
194
stackError: new Error(),
195
value: thenable,
@@ -199,6 +202,7 @@ function use<T>(usable: Usable<T>): T {
202
const value = readContext(context);
203
204
hookLog.push({
205
+ displayName: context.displayName || 'Context',
206
primitive: 'Context (use)',
207
stackError: new Error(),
208
value,
@@ -215,6 +219,7 @@ function use<T>(usable: Usable<T>): T {
219
220
function useContext<T>(context: ReactContext<T>): T {
221
hookLog.push({
222
+ displayName: context.displayName || null,
223
primitive: 'Context',
224
stackError: new Error(),
225
value: context._currentValue,
@@ -235,6 +240,7 @@ function useState<S>(
240
initialState()
241
: initialState;
242
hookLog.push({
243
+ displayName: null,
244
primitive: 'State',
245
stackError: new Error(),
246
value: state,
@@ -256,6 +262,7 @@ function useReducer<S, I, A>(
262
state = init !== undefined ? init(initialArg) : ((initialArg: any): S);
263
}
264
hookLog.push({
265
+ displayName: null,
266
primitive: 'Reducer',
267
stackError: new Error(),
268
value: state,
@@ -268,6 +275,7 @@ function useRef<T>(initialValue: T): {current: T} {
275
const hook = nextHook();
276
const ref = hook !== null ? hook.memoizedState : {current: initialValue};
277
hookLog.push({
278
+ displayName: null,
279
primitive: 'Ref',
280
stackError: new Error(),
281
value: ref.current,
@@ -279,6 +287,7 @@ function useRef<T>(initialValue: T): {current: T} {
287
function useCacheRefresh(): () => void {
288
const hook = nextHook();
289
hookLog.push({
290
+ displayName: null,
291
primitive: 'CacheRefresh',
292
stackError: new Error(),
293
value: hook !== null ? hook.memoizedState : function refresh() {},
@@ -293,6 +302,7 @@ function useLayoutEffect(
302
): void {
303
nextHook();
304
hookLog.push({
305
+ displayName: null,
306
primitive: 'LayoutEffect',
307
stackError: new Error(),
308
value: create,
@@ -306,6 +316,7 @@ function useInsertionEffect(
316
): void {
317
nextHook();
318
hookLog.push({
319
+ displayName: null,
320
primitive: 'InsertionEffect',
321
stackError: new Error(),
322
value: create,
@@ -319,6 +330,7 @@ function useEffect(
330
): void {
331
nextHook();
332
hookLog.push({
333
+ displayName: null,
334
primitive: 'Effect',
335
stackError: new Error(),
336
value: create,
@@ -341,6 +353,7 @@ function useImperativeHandle<T>(
353
instance = ref.current;
354
}
355
hookLog.push({
356
+ displayName: null,
357
primitive: 'ImperativeHandle',
358
stackError: new Error(),
359
value: instance,
@@ -350,6 +363,7 @@ function useImperativeHandle<T>(
363
364
function useDebugValue(value: any, formatterFn: ?(value: any) => any) {
365
hookLog.push({
366
+ displayName: null,
367
primitive: 'DebugValue',
368
stackError: new Error(),
369
value: typeof formatterFn === 'function' ? formatterFn(value) : value,
@@ -360,6 +374,7 @@ function useDebugValue(value: any, formatterFn: ?(value: any) => any) {
374
function useCallback<T>(callback: T, inputs: Array<mixed> | void | null): T {
375
const hook = nextHook();
376
hookLog.push({
377
+ displayName: null,
378
primitive: 'Callback',
379
stackError: new Error(),
380
value: hook !== null ? hook.memoizedState[0] : callback,
@@ -375,6 +390,7 @@ function useMemo<T>(
390
const hook = nextHook();
391
const value = hook !== null ? hook.memoizedState[0] : nextCreate();
392
hookLog.push({
393
+ displayName: null,
394
primitive: 'Memo',
395
stackError: new Error(),
396
value,
@@ -395,6 +411,7 @@ function useSyncExternalStore<T>(
411
nextHook(); // Effect
412
const value = getSnapshot();
413
hookLog.push({
414
+ displayName: null,
415
primitive: 'SyncExternalStore',
416
stackError: new Error(),
417
value,
@@ -413,6 +430,7 @@ function useTransition(): [
430
nextHook(); // State
431
nextHook(); // Callback
432
hookLog.push({
433
+ displayName: null,
434
primitive: 'Transition',
435
stackError: new Error(),
436
value: undefined,
@@ -424,6 +442,7 @@ function useTransition(): [
442
function useDeferredValue<T>(value: T, initialValue?: T): T {
443
const hook = nextHook();
444
hookLog.push({
445
+ displayName: null,
446
primitive: 'DeferredValue',
447
stackError: new Error(),
448
value: hook !== null ? hook.memoizedState : value,
@@ -436,6 +455,7 @@ function useId(): string {
455
const hook = nextHook();
456
const id = hook !== null ? hook.memoizedState : '';
457
hookLog.push({
458
+ displayName: null,
459
primitive: 'Id',
460
stackError: new Error(),
461
value: id,
@@ -485,6 +505,7 @@ function useOptimistic<S, A>(
505
state = passthrough;
506
}
507
hookLog.push({
508
+ displayName: null,
509
primitive: 'Optimistic',
510
stackError: new Error(),
511
value: state,
@@ -507,6 +528,7 @@ function useFormState<S, P>(
528
state = initialState;
529
}
530
hookLog.push({
531
+ displayName: null,
532
primitive: 'FormState',
533
stackError: new Error(),
534
value: state,
@@ -780,7 +802,7 @@ function buildTree(
802
}
803
prevStack = stack;
804
}
783
- const {primitive, debugInfo} = hook;
805
+ const {displayName, primitive, debugInfo} = hook;
806
807
// For now, the "id" of stateful hooks is just the stateful hook index.
808
// Custom hooks have no ids, nor do non-stateful native hooks (e.g. Context, DebugValue).
@@ -795,7 +817,7 @@ function buildTree(
817
818
// For the time being, only State and Reducer hooks support runtime overrides.
819
const isStateEditable = primitive === 'Reducer' || primitive === 'State';
798
- const name = primitive === 'Context (use)' ? 'Context' : primitive;
820
+ const name = displayName || primitive;
821
const levelChild: HooksNode = {
822
id,
823
isStateEditable,
packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js
+17
@@ -772,8 +772,11 @@ describe('ReactHooksInspectionIntegration', () => {
772
773
it('should inspect the value of the current provider in useContext', () => {
774
const MyContext = React.createContext('default');
775
+ const ThemeContext = React.createContext('default');
776
+ ThemeContext.displayName = 'Theme';
777
function Foo(props) {
778
const value = React.useContext(MyContext);
779
+ React.useContext(ThemeContext);
780
return <div>{value}</div>;
781
}
782
const renderer = ReactTestRenderer.create(
@@ -799,6 +802,20 @@ describe('ReactHooksInspectionIntegration', () => {
802
"subHooks": [],
803
"value": "contextual",
804
},
805
+ {
806
+ "debugInfo": null,
807
+ "hookSource": {
808
+ "columnNumber": 0,
809
+ "fileName": "**",
810
+ "functionName": "Foo",
811
+ "lineNumber": 0,
812
+ },
813
+ "id": null,
814
+ "isStateEditable": false,
815
+ "name": "Theme",
816
+ "subHooks": [],
817
+ "value": "default",
818
+ },
819
]
820
`);
821
});