@@ -47,6 +47,7 @@ type HookLogEntry = {
47
47
stackError : Error ,
48
48
value : mixed ,
49
49
debugInfo : ReactDebugInfo | null ,
50
+ dispatcherMethodName : string ,
50
51
} ;
51
52
52
53
let hookLog : Array < HookLogEntry > = [ ] ;
@@ -127,6 +128,8 @@ function getPrimitiveStackCache(): Map<string, Array<any>> {
127
128
) ;
128
129
} catch ( x ) { }
129
130
}
131
+
132
+ Dispatcher . useId ( ) ;
130
133
} finally {
131
134
readHookLog = hookLog ;
132
135
hookLog = [ ] ;
@@ -203,6 +206,7 @@ function use<T>(usable: Usable<T>): T {
203
206
value : fulfilledValue ,
204
207
debugInfo :
205
208
thenable . _debugInfo === undefined ? null : thenable . _debugInfo ,
209
+ dispatcherMethodName : 'use' ,
206
210
} ) ;
207
211
return fulfilledValue ;
208
212
}
@@ -220,6 +224,7 @@ function use<T>(usable: Usable<T>): T {
220
224
value : thenable ,
221
225
debugInfo :
222
226
thenable . _debugInfo === undefined ? null : thenable . _debugInfo ,
227
+ dispatcherMethodName : 'use' ,
223
228
} );
224
229
throw SuspenseException;
225
230
} else if ( usable . $$typeof === REACT_CONTEXT_TYPE ) {
@@ -232,6 +237,7 @@ function use<T>(usable: Usable<T>): T {
232
237
stackError : new Error ( ) ,
233
238
value,
234
239
debugInfo : null ,
240
+ dispatcherMethodName : 'use' ,
235
241
} ) ;
236
242
237
243
return value ;
@@ -250,6 +256,7 @@ function useContext<T>(context: ReactContext<T>): T {
250
256
stackError : new Error ( ) ,
251
257
value : value ,
252
258
debugInfo : null ,
259
+ dispatcherMethodName : 'useContext' ,
253
260
} ) ;
254
261
return value ;
255
262
}
@@ -271,6 +278,7 @@ function useState<S>(
271
278
stackError : new Error ( ) ,
272
279
value : state ,
273
280
debugInfo : null ,
281
+ dispatcherMethodName : 'useState' ,
274
282
} ) ;
275
283
return [ state , ( action : BasicStateAction < S > ) => { } ] ;
276
284
}
@@ -293,6 +301,7 @@ function useReducer<S, I, A>(
293
301
stackError : new Error ( ) ,
294
302
value : state ,
295
303
debugInfo : null ,
304
+ dispatcherMethodName : 'useReducer' ,
296
305
} ) ;
297
306
return [ state , ( action : A ) => { } ] ;
298
307
}
@@ -306,6 +315,7 @@ function useRef<T>(initialValue: T): {current: T} {
306
315
stackError : new Error ( ) ,
307
316
value : ref . current ,
308
317
debugInfo : null ,
318
+ dispatcherMethodName : 'useRef' ,
309
319
} ) ;
310
320
return ref ;
311
321
}
@@ -318,6 +328,7 @@ function useCacheRefresh(): () => void {
318
328
stackError : new Error ( ) ,
319
329
value : hook !== null ? hook . memoizedState : function refresh ( ) { } ,
320
330
debugInfo : null ,
331
+ dispatcherMethodName : 'useCacheRefresh' ,
321
332
} ) ;
322
333
return ( ) = > { } ;
323
334
}
@@ -333,6 +344,7 @@ function useLayoutEffect(
333
344
stackError : new Error ( ) ,
334
345
value : create ,
335
346
debugInfo : null ,
347
+ dispatcherMethodName : 'useLayoutEffect' ,
336
348
} ) ;
337
349
}
338
350
@@ -347,6 +359,7 @@ function useInsertionEffect(
347
359
stackError : new Error ( ) ,
348
360
value : create ,
349
361
debugInfo : null ,
362
+ dispatcherMethodName : 'useInsertionEffect' ,
350
363
} ) ;
351
364
}
352
365
@@ -361,6 +374,7 @@ function useEffect(
361
374
stackError : new Error ( ) ,
362
375
value : create ,
363
376
debugInfo : null ,
377
+ dispatcherMethodName : 'useEffect' ,
364
378
} ) ;
365
379
}
366
380
@@ -384,6 +398,7 @@ function useImperativeHandle<T>(
384
398
stackError : new Error ( ) ,
385
399
value : instance ,
386
400
debugInfo : null ,
401
+ dispatcherMethodName : 'useImperativeHandle' ,
387
402
} );
388
403
}
389
404
@@ -394,6 +409,7 @@ function useDebugValue(value: any, formatterFn: ?(value: any) => any) {
394
409
stackError : new Error ( ) ,
395
410
value : typeof formatterFn === 'function' ? formatterFn ( value ) : value ,
396
411
debugInfo : null ,
412
+ dispatcherMethodName : 'useDebugValue' ,
397
413
} ) ;
398
414
}
399
415
@@ -405,6 +421,7 @@ function useCallback<T>(callback: T, inputs: Array<mixed> | void | null): T {
405
421
stackError : new Error ( ) ,
406
422
value : hook !== null ? hook . memoizedState [ 0 ] : callback ,
407
423
debugInfo : null ,
424
+ dispatcherMethodName : 'useCallback' ,
408
425
} ) ;
409
426
return callback ;
410
427
}
@@ -421,6 +438,7 @@ function useMemo<T>(
421
438
stackError : new Error ( ) ,
422
439
value,
423
440
debugInfo : null ,
441
+ dispatcherMethodName : 'useMemo' ,
424
442
} ) ;
425
443
return value ;
426
444
}
@@ -442,6 +460,7 @@ function useSyncExternalStore<T>(
442
460
stackError : new Error ( ) ,
443
461
value,
444
462
debugInfo : null ,
463
+ dispatcherMethodName : 'useSyncExternalStore' ,
445
464
} ) ;
446
465
return value ;
447
466
}
@@ -464,6 +483,7 @@ function useTransition(): [
464
483
stackError : new Error ( ) ,
465
484
value : isPending ,
466
485
debugInfo : null ,
486
+ dispatcherMethodName : 'useTransition' ,
467
487
} ) ;
468
488
return [ isPending , ( ) => { } ] ;
469
489
}
@@ -477,6 +497,7 @@ function useDeferredValue<T>(value: T, initialValue?: T): T {
477
497
stackError : new Error ( ) ,
478
498
value : prevValue ,
479
499
debugInfo : null ,
500
+ dispatcherMethodName : 'useDeferredValue' ,
480
501
} ) ;
481
502
return prevValue ;
482
503
}
@@ -490,6 +511,7 @@ function useId(): string {
490
511
stackError : new Error ( ) ,
491
512
value : id ,
492
513
debugInfo : null ,
514
+ dispatcherMethodName : 'useId' ,
493
515
} ) ;
494
516
return id ;
495
517
}
@@ -540,6 +562,7 @@ function useOptimistic<S, A>(
540
562
stackError : new Error ( ) ,
541
563
value : state ,
542
564
debugInfo : null ,
565
+ dispatcherMethodName : 'useOptimistic' ,
543
566
} ) ;
544
567
return [ state , ( action : A ) => { } ] ;
545
568
}
@@ -599,6 +622,7 @@ function useFormState<S, P>(
599
622
stackError : stackError ,
600
623
value : value ,
601
624
debugInfo : debugInfo ,
625
+ dispatcherMethodName : 'useFormState' ,
602
626
} );
603
627
604
628
if (error !== null) {
@@ -685,8 +709,7 @@ export type HooksTree = Array<HooksNode>;
685
709
// of a hook call. A simple way to demonstrate this is wrapping `new Error()`
686
710
// in a wrapper constructor like a polyfill. That'll add an extra frame.
687
711
// Similar things can happen with the call to the dispatcher. The top frame
688
- // may not be the primitive. Likewise the primitive can have fewer stack frames
689
- // such as when a call to useState got inlined to use dispatcher.useState.
712
+ // may not be the primitive.
690
713
//
691
714
// We also can't assume that the last frame of the root call is the same
692
715
// frame as the last frame of the hook call because long stack traces can be
@@ -736,26 +759,16 @@ function findCommonAncestorIndex(rootStack: any, hookStack: any) {
736
759
return - 1 ;
737
760
}
738
761
739
- function isReactWrapper ( functionName : any , primitiveName : string ) {
762
+ function isReactWrapper ( functionName : any , wrapperName : string ) {
740
763
if ( ! functionName ) {
741
764
return false ;
742
765
}
743
- switch (primitiveName) {
744
- case 'Context' :
745
- case 'Context (use)' :
746
- case 'Promise' :
747
- case 'Unresolved' :
748
- if ( functionName . endsWith ( 'use' ) ) {
749
- return true ;
750
- }
751
- }
752
- const expectedPrimitiveName = 'use' + primitiveName;
753
- if (functionName.length < expectedPrimitiveName . length ) {
766
+ if (functionName.length < wrapperName . length ) {
754
767
return false ;
755
768
}
756
769
return (
757
- functionName . lastIndexOf ( expectedPrimitiveName ) ===
758
- functionName . length - expectedPrimitiveName . length
770
+ functionName . lastIndexOf ( wrapperName ) ===
771
+ functionName . length - wrapperName . length
759
772
) ;
760
773
}
761
774
@@ -767,17 +780,18 @@ function findPrimitiveIndex(hookStack: any, hook: HookLogEntry) {
767
780
}
768
781
for ( let i = 0 ; i < primitiveStack . length && i < hookStack . length ; i ++ ) {
769
782
if ( primitiveStack [ i ] . source !== hookStack [ i ] . source ) {
770
- // If the next two frames are functions called `useX` then we assume that they're part of the
771
- // wrappers that the React packager or other packages adds around the dispatcher.
783
+ // If the next frame is a method from the dispatcher, we
784
+ // assume that the next frame after that is the actual public API call.
785
+ // This prohibits nesting dispatcher calls in hooks.
772
786
if (
773
787
i < hookStack . length - 1 &&
774
- isReactWrapper ( hookStack [ i ] . functionName , hook . primitive )
788
+ isReactWrapper ( hookStack [ i ] . functionName , hook . dispatcherMethodName )
775
789
) {
776
790
i ++ ;
777
791
}
778
792
if (
779
793
i < hookStack . length - 1 &&
780
- isReactWrapper ( hookStack [ i ] . functionName , hook . primitive )
794
+ isReactWrapper ( hookStack [ i ] . functionName , hook . dispatcherMethodName )
781
795
) {
782
796
i ++ ;
783
797
}
@@ -801,18 +815,26 @@ function parseTrimmedStack(rootStack: any, hook: HookLogEntry) {
801
815
// Something went wrong. Give up.
802
816
return null ;
803
817
}
804
- return hookStack . slice ( primitiveIndex , rootIndex - 1 ) ;
818
+ return [
819
+ hookStack [ primitiveIndex - 1 ] ,
820
+ hookStack . slice ( primitiveIndex , rootIndex - 1 ) ,
821
+ ] ;
805
822
}
806
823
807
- function parseCustomHookName ( functionName : void | string ) : string {
824
+ function parseHookName ( functionName : void | string ) : string {
808
825
if ( ! functionName ) {
809
826
return '' ;
810
827
}
811
828
let startIndex = functionName . lastIndexOf ( '.' ) ;
812
829
if ( startIndex === - 1 ) {
813
830
startIndex = 0 ;
831
+ } else {
832
+ startIndex + = 1 ;
814
833
}
815
834
if ( functionName . slice ( startIndex , startIndex + 3 ) === 'use' ) {
835
+ if ( functionName . length - startIndex === 3 ) {
836
+ return 'Use' ;
837
+ }
816
838
startIndex += 3 ;
817
839
}
818
840
return functionName . slice ( startIndex ) ;
@@ -829,8 +851,17 @@ function buildTree(
829
851
const stackOfChildren = [];
830
852
for (let i = 0; i < readHookLog . length ; i ++ ) {
831
853
const hook = readHookLog [ i ] ;
832
- const stack = parseTrimmedStack ( rootStack , hook ) ;
833
- if ( stack !== null ) {
854
+ const parseResult = parseTrimmedStack ( rootStack , hook ) ;
855
+ let displayName = hook . displayName ;
856
+ if ( parseResult !== null ) {
857
+ const [ primitiveFrame , stack ] = parseResult ;
858
+ if ( hook . displayName === null ) {
859
+ displayName =
860
+ parseHookName ( primitiveFrame . functionName ) ||
861
+ // Older versions of React do not have sourcemaps.
862
+ // In those versions there was always a 1:1 mapping between wrapper and dispatcher method.
863
+ parseHookName ( hook . dispatcherMethodName ) ;
864
+ }
834
865
// Note: The indices 0 <= n < length-1 will contain the names.
835
866
// The indices 1 <= n < length will contain the source locations.
836
867
// That's why we get the name from n - 1 and don't check the source
@@ -860,7 +891,7 @@ function buildTree(
860
891
const levelChild: HooksNode = {
861
892
id : null ,
862
893
isStateEditable : false ,
863
- name : parseCustomHookName ( stack [ j - 1 ] . functionName ) ,
894
+ name : parseHookName ( stack [ j - 1 ] . functionName ) ,
864
895
value : undefined ,
865
896
subHooks : children ,
866
897
debugInfo : null ,
@@ -878,7 +909,7 @@ function buildTree(
878
909
}
879
910
prevStack = stack ;
880
911
}
881
- const { displayName , primitive , debugInfo } = hook;
912
+ const { primitive , debugInfo } = hook;
882
913
883
914
// For now, the "id" of stateful hooks is just the stateful hook index.
884
915
// Custom hooks have no ids, nor do non-stateful native hooks (e.g. Context, DebugValue).
@@ -893,11 +924,11 @@ function buildTree(
893
924
894
925
// For the time being, only State and Reducer hooks support runtime overrides.
895
926
const isStateEditable = primitive === 'Reducer' || primitive === 'State';
896
- const name = displayName || primitive;
927
+
897
928
const levelChild: HooksNode = {
898
929
id ,
899
930
isStateEditable ,
900
- name : name ,
931
+ name : displayName || 'Unknown' ,
901
932
value : hook . value ,
902
933
subHooks : [ ] ,
903
934
debugInfo : debugInfo ,
@@ -910,6 +941,7 @@ function buildTree(
910
941
fileName : null ,
911
942
columnNumber : null ,
912
943
} ;
944
+ const stack = parseResult !== null ? parseResult[1] : null;
913
945
if (stack && stack . length >= 1 ) {
914
946
const stackFrame = stack [ 0 ] ;
915
947
hookSource . lineNumber = stackFrame . lineNumber ;
0 commit comments