@@ -45,6 +45,7 @@ type HookLogEntry = {
45
45
stackError : Error ,
46
46
value : mixed ,
47
47
debugInfo : ReactDebugInfo | null ,
48
+ dispatcherMethodName : string ,
48
49
} ;
49
50
50
51
let hookLog : Array < HookLogEntry > = [ ] ;
@@ -130,6 +131,7 @@ function getPrimitiveStackCache(): Map<string, Array<any>> {
130
131
// This type check is for Flow only.
131
132
Dispatcher . useHostTransitionStatus ( ) ;
132
133
}
134
+ Dispatcher . useId ( ) ;
133
135
} finally {
134
136
readHookLog = hookLog ;
135
137
hookLog = [ ] ;
@@ -200,6 +202,7 @@ function use<T>(usable: Usable<T>): T {
200
202
value : fulfilledValue ,
201
203
debugInfo :
202
204
thenable . _debugInfo === undefined ? null : thenable . _debugInfo ,
205
+ dispatcherMethodName : 'use' ,
203
206
} ) ;
204
207
return fulfilledValue ;
205
208
}
@@ -217,6 +220,7 @@ function use<T>(usable: Usable<T>): T {
217
220
value : thenable ,
218
221
debugInfo :
219
222
thenable . _debugInfo === undefined ? null : thenable . _debugInfo ,
223
+ dispatcherMethodName : 'use' ,
220
224
} );
221
225
throw SuspenseException;
222
226
} else if ( usable . $$typeof === REACT_CONTEXT_TYPE ) {
@@ -229,6 +233,7 @@ function use<T>(usable: Usable<T>): T {
229
233
stackError : new Error ( ) ,
230
234
value,
231
235
debugInfo : null ,
236
+ dispatcherMethodName : 'use' ,
232
237
} ) ;
233
238
234
239
return value ;
@@ -247,6 +252,7 @@ function useContext<T>(context: ReactContext<T>): T {
247
252
stackError : new Error ( ) ,
248
253
value : value ,
249
254
debugInfo : null ,
255
+ dispatcherMethodName : 'useContext' ,
250
256
} ) ;
251
257
return value ;
252
258
}
@@ -268,6 +274,7 @@ function useState<S>(
268
274
stackError : new Error ( ) ,
269
275
value : state ,
270
276
debugInfo : null ,
277
+ dispatcherMethodName : 'useState' ,
271
278
} ) ;
272
279
return [ state , ( action : BasicStateAction < S > ) => { } ] ;
273
280
}
@@ -290,6 +297,7 @@ function useReducer<S, I, A>(
290
297
stackError : new Error ( ) ,
291
298
value : state ,
292
299
debugInfo : null ,
300
+ dispatcherMethodName : 'useReducer' ,
293
301
} ) ;
294
302
return [ state , ( action : A ) => { } ] ;
295
303
}
@@ -303,6 +311,7 @@ function useRef<T>(initialValue: T): {current: T} {
303
311
stackError : new Error ( ) ,
304
312
value : ref . current ,
305
313
debugInfo : null ,
314
+ dispatcherMethodName : 'useRef' ,
306
315
} ) ;
307
316
return ref ;
308
317
}
@@ -315,6 +324,7 @@ function useCacheRefresh(): () => void {
315
324
stackError : new Error ( ) ,
316
325
value : hook !== null ? hook . memoizedState : function refresh ( ) { } ,
317
326
debugInfo : null ,
327
+ dispatcherMethodName : 'useCacheRefresh' ,
318
328
} ) ;
319
329
return ( ) = > { } ;
320
330
}
@@ -330,6 +340,7 @@ function useLayoutEffect(
330
340
stackError : new Error ( ) ,
331
341
value : create ,
332
342
debugInfo : null ,
343
+ dispatcherMethodName : 'useLayoutEffect' ,
333
344
} ) ;
334
345
}
335
346
@@ -344,6 +355,7 @@ function useInsertionEffect(
344
355
stackError : new Error ( ) ,
345
356
value : create ,
346
357
debugInfo : null ,
358
+ dispatcherMethodName : 'useInsertionEffect' ,
347
359
} ) ;
348
360
}
349
361
@@ -358,6 +370,7 @@ function useEffect(
358
370
stackError : new Error ( ) ,
359
371
value : create ,
360
372
debugInfo : null ,
373
+ dispatcherMethodName : 'useEffect' ,
361
374
} ) ;
362
375
}
363
376
@@ -381,6 +394,7 @@ function useImperativeHandle<T>(
381
394
stackError : new Error ( ) ,
382
395
value : instance ,
383
396
debugInfo : null ,
397
+ dispatcherMethodName : 'useImperativeHandle' ,
384
398
} );
385
399
}
386
400
@@ -391,6 +405,7 @@ function useDebugValue(value: any, formatterFn: ?(value: any) => any) {
391
405
stackError : new Error ( ) ,
392
406
value : typeof formatterFn === 'function' ? formatterFn ( value ) : value ,
393
407
debugInfo : null ,
408
+ dispatcherMethodName : 'useDebugValue' ,
394
409
} ) ;
395
410
}
396
411
@@ -402,6 +417,7 @@ function useCallback<T>(callback: T, inputs: Array<mixed> | void | null): T {
402
417
stackError : new Error ( ) ,
403
418
value : hook !== null ? hook . memoizedState [ 0 ] : callback ,
404
419
debugInfo : null ,
420
+ dispatcherMethodName : 'useCallback' ,
405
421
} ) ;
406
422
return callback ;
407
423
}
@@ -418,6 +434,7 @@ function useMemo<T>(
418
434
stackError : new Error ( ) ,
419
435
value,
420
436
debugInfo : null ,
437
+ dispatcherMethodName : 'useMemo' ,
421
438
} ) ;
422
439
return value ;
423
440
}
@@ -439,6 +456,7 @@ function useSyncExternalStore<T>(
439
456
stackError : new Error ( ) ,
440
457
value,
441
458
debugInfo : null ,
459
+ dispatcherMethodName : 'useSyncExternalStore' ,
442
460
} ) ;
443
461
return value ;
444
462
}
@@ -458,6 +476,7 @@ function useTransition(): [
458
476
stackError : new Error ( ) ,
459
477
value : undefined ,
460
478
debugInfo : null ,
479
+ dispatcherMethodName : 'useTransition' ,
461
480
} ) ;
462
481
return [ false , callback => { } ] ;
463
482
}
@@ -470,6 +489,7 @@ function useDeferredValue<T>(value: T, initialValue?: T): T {
470
489
stackError : new Error ( ) ,
471
490
value : hook !== null ? hook . memoizedState : value ,
472
491
debugInfo : null ,
492
+ dispatcherMethodName : 'useDeferredValue' ,
473
493
} ) ;
474
494
return value ;
475
495
}
@@ -483,6 +503,7 @@ function useId(): string {
483
503
stackError : new Error ( ) ,
484
504
value : id ,
485
505
debugInfo : null ,
506
+ dispatcherMethodName : 'useId' ,
486
507
} ) ;
487
508
return id ;
488
509
}
@@ -533,6 +554,7 @@ function useOptimistic<S, A>(
533
554
stackError : new Error ( ) ,
534
555
value : state ,
535
556
debugInfo : null ,
557
+ dispatcherMethodName : 'useOptimistic' ,
536
558
} ) ;
537
559
return [ state , ( action : A ) => { } ] ;
538
560
}
@@ -591,6 +613,7 @@ function useFormState<S, P>(
591
613
stackError : stackError ,
592
614
value : value ,
593
615
debugInfo : debugInfo ,
616
+ dispatcherMethodName : 'useFormState' ,
594
617
} );
595
618
596
619
if (error !== null) {
@@ -618,6 +641,7 @@ function useHostTransitionStatus(): TransitionStatus {
618
641
stackError : new Error ( ) ,
619
642
value : status ,
620
643
debugInfo : null ,
644
+ dispatcherMethodName : 'HostTransitionStatus' ,
621
645
} ) ;
622
646
623
647
return status ;
@@ -696,8 +720,7 @@ export type HooksTree = Array<HooksNode>;
696
720
// of a hook call. A simple way to demonstrate this is wrapping `new Error()`
697
721
// in a wrapper constructor like a polyfill. That'll add an extra frame.
698
722
// Similar things can happen with the call to the dispatcher. The top frame
699
- // may not be the primitive. Likewise the primitive can have fewer stack frames
700
- // such as when a call to useState got inlined to use dispatcher.useState.
723
+ // may not be the primitive.
701
724
//
702
725
// We also can't assume that the last frame of the root call is the same
703
726
// frame as the last frame of the hook call because long stack traces can be
@@ -747,26 +770,16 @@ function findCommonAncestorIndex(rootStack: any, hookStack: any) {
747
770
return - 1 ;
748
771
}
749
772
750
- function isReactWrapper ( functionName : any , primitiveName : string ) {
773
+ function isReactWrapper ( functionName : any , wrapperName : string ) {
751
774
if ( ! functionName ) {
752
775
return false ;
753
776
}
754
- switch (primitiveName) {
755
- case 'Context' :
756
- case 'Context (use)' :
757
- case 'Promise' :
758
- case 'Unresolved' :
759
- if ( functionName . endsWith ( 'use' ) ) {
760
- return true ;
761
- }
762
- }
763
- const expectedPrimitiveName = 'use' + primitiveName;
764
- if (functionName.length < expectedPrimitiveName . length ) {
777
+ if (functionName.length < wrapperName . length ) {
765
778
return false ;
766
779
}
767
780
return (
768
- functionName . lastIndexOf ( expectedPrimitiveName ) ===
769
- functionName . length - expectedPrimitiveName . length
781
+ functionName . lastIndexOf ( wrapperName ) ===
782
+ functionName . length - wrapperName . length
770
783
) ;
771
784
}
772
785
@@ -778,18 +791,14 @@ function findPrimitiveIndex(hookStack: any, hook: HookLogEntry) {
778
791
}
779
792
for ( let i = 0 ; i < primitiveStack . length && i < hookStack . length ; i ++ ) {
780
793
if ( primitiveStack [ i ] . source !== hookStack [ i ] . source ) {
781
- // If the next two frames are functions called `useX` then we assume that they're part of the
782
- // wrappers that the React packager or other packages adds around the dispatcher.
794
+ // If the next frame is a method from the dispatcher, we
795
+ // assume that the next frame after that is the actual public API call.
796
+ // This prohibits nesting dispatcher calls in hooks.
783
797
if (
784
798
i < hookStack . length - 1 &&
785
- isReactWrapper ( hookStack [ i ] . functionName , hook . primitive )
799
+ isReactWrapper ( hookStack [ i ] . functionName , hook . dispatcherMethodName )
786
800
) {
787
801
i ++ ;
788
- }
789
- if (
790
- i < hookStack . length - 1 &&
791
- isReactWrapper ( hookStack [ i ] . functionName , hook . primitive )
792
- ) {
793
802
i ++ ;
794
803
}
795
804
return i ;
@@ -812,16 +821,21 @@ function parseTrimmedStack(rootStack: any, hook: HookLogEntry) {
812
821
// Something went wrong. Give up.
813
822
return null ;
814
823
}
815
- return hookStack . slice ( primitiveIndex , rootIndex - 1 ) ;
824
+ return [
825
+ hookStack [ primitiveIndex - 1 ] ,
826
+ hookStack . slice ( primitiveIndex , rootIndex - 1 ) ,
827
+ ] ;
816
828
}
817
829
818
- function parseCustomHookName ( functionName : void | string ) : string {
830
+ function parseHookName ( functionName : void | string ) : string {
819
831
if ( ! functionName ) {
820
832
return '' ;
821
833
}
822
834
let startIndex = functionName . lastIndexOf ( '.' ) ;
823
835
if ( startIndex === - 1 ) {
824
836
startIndex = 0 ;
837
+ } else {
838
+ startIndex + = 1 ;
825
839
}
826
840
if ( functionName . slice ( startIndex , startIndex + 3 ) === 'use' ) {
827
841
startIndex += 3 ;
@@ -840,8 +854,14 @@ function buildTree(
840
854
const stackOfChildren = [];
841
855
for (let i = 0; i < readHookLog . length ; i ++ ) {
842
856
const hook = readHookLog [ i ] ;
843
- const stack = parseTrimmedStack ( rootStack , hook ) ;
844
- if ( stack !== null ) {
857
+ const parseResult = parseTrimmedStack ( rootStack , hook ) ;
858
+ let displayName = hook . displayName ;
859
+ if ( parseResult !== null ) {
860
+ const [ primitiveFrame , stack ] = parseResult ;
861
+ if ( hook . displayName === null ) {
862
+ // TODO: Support older versions of React without sourcemaps by using the primitive name if primitiveFrame.functionName does not look like a hook.
863
+ displayName = parseHookName ( primitiveFrame . functionName ) ;
864
+ }
845
865
// Note: The indices 0 <= n < length-1 will contain the names.
846
866
// The indices 1 <= n < length will contain the source locations.
847
867
// That's why we get the name from n - 1 and don't check the source
@@ -871,7 +891,7 @@ function buildTree(
871
891
const levelChild: HooksNode = {
872
892
id : null ,
873
893
isStateEditable : false ,
874
- name : parseCustomHookName ( stack [ j - 1 ] . functionName ) ,
894
+ name : parseHookName ( stack [ j - 1 ] . functionName ) ,
875
895
value : undefined ,
876
896
subHooks : children ,
877
897
debugInfo : null ,
@@ -889,7 +909,7 @@ function buildTree(
889
909
}
890
910
prevStack = stack ;
891
911
}
892
- const { displayName , primitive , debugInfo } = hook;
912
+ const { primitive , debugInfo } = hook;
893
913
894
914
// For now, the "id" of stateful hooks is just the stateful hook index.
895
915
// Custom hooks have no ids, nor do non-stateful native hooks (e.g. Context, DebugValue).
@@ -905,11 +925,11 @@ function buildTree(
905
925
906
926
// For the time being, only State and Reducer hooks support runtime overrides.
907
927
const isStateEditable = primitive === 'Reducer' || primitive === 'State';
908
- const name = displayName || primitive;
928
+
909
929
const levelChild: HooksNode = {
910
930
id ,
911
931
isStateEditable ,
912
- name : name ,
932
+ name : displayName || 'Unknown' ,
913
933
value : hook . value ,
914
934
subHooks : [ ] ,
915
935
debugInfo : debugInfo ,
@@ -922,6 +942,7 @@ function buildTree(
922
942
fileName : null ,
923
943
columnNumber : null ,
924
944
} ;
945
+ const stack = parseResult !== null ? parseResult[1] : null;
925
946
if (stack && stack . length >= 1 ) {
926
947
const stackFrame = stack [ 0 ] ;
927
948
hookSource . lineNumber = stackFrame . lineNumber ;
0 commit comments