@@ -355,12 +355,25 @@ export function setSignature(
355
355
getCustomHooks ?: ( ) => Array < Function > ,
356
356
): void {
357
357
if ( __DEV__ ) {
358
- allSignaturesByType . set ( type , {
359
- forceReset,
360
- ownKey : key ,
361
- fullKey : null ,
362
- getCustomHooks : getCustomHooks || ( ( ) => [ ] ) ,
363
- } ) ;
358
+ if ( ! allSignaturesByType . has ( type ) ) {
359
+ allSignaturesByType . set ( type , {
360
+ forceReset,
361
+ ownKey : key ,
362
+ fullKey : null ,
363
+ getCustomHooks : getCustomHooks || ( ( ) => [ ] ) ,
364
+ } ) ;
365
+ }
366
+ // Visit inner types because we might not have signed them.
367
+ if (typeof type === 'object' && type !== null ) {
368
+ switch ( getProperty ( type , '$$typeof' ) ) {
369
+ case REACT_FORWARD_REF_TYPE :
370
+ setSignature ( type . render , key , forceReset , getCustomHooks ) ;
371
+ break ;
372
+ case REACT_MEMO_TYPE :
373
+ setSignature ( type . type , key , forceReset , getCustomHooks ) ;
374
+ break ;
375
+ }
376
+ }
364
377
} else {
365
378
throw new Error (
366
379
'Unexpected call to React Refresh in a production environment.' ,
@@ -609,57 +622,58 @@ export function _getMountedRootCount() {
609
622
// function Hello() {
610
623
// const [foo, setFoo] = useState(0);
611
624
// const value = useCustomHook();
612
- // _s(); /* Second call triggers collecting the custom Hook list.
625
+ // _s(); /* Call without arguments triggers collecting the custom Hook list.
613
626
// * This doesn't happen during the module evaluation because we
614
627
// * don't want to change the module order with inline requires.
615
628
// * Next calls are noops. */
616
629
// return <h1>Hi</h1>;
617
630
// }
618
631
//
619
- // /* First call specifies the signature: */
632
+ // /* Call with arguments attaches the signature to the type : */
620
633
// _s(
621
634
// Hello,
622
635
// 'useState{[foo, setFoo]}(0)',
623
636
// () => [useCustomHook], /* Lazy to avoid triggering inline requires */
624
637
// );
625
- type SignatureStatus = 'needsSignature ' | 'needsCustomHooks ' | 'resolved ';
626
638
export function createSignatureFunctionForTransform ( ) {
627
639
if ( __DEV__ ) {
628
- // We'll fill in the signature in two steps.
629
- // First, we'll know the signature itself. This happens outside the component.
630
- // Then, we'll know the references to custom Hooks. This happens inside the component.
631
- // After that, the returned function will be a fast path no-op.
632
- let status : SignatureStatus = 'needsSignature' ;
633
640
let savedType ;
634
641
let hasCustomHooks ;
642
+ let didCollectHooks = false ;
635
643
return function < T > (
636
644
type: T,
637
645
key: string,
638
646
forceReset?: boolean,
639
647
getCustomHooks?: () => Array < Function > ,
640
- ) : T {
641
- switch ( status ) {
642
- case 'needsSignature' :
643
- if ( type !== undefined ) {
644
- // If we received an argument, this is the initial registration call.
645
- savedType = type ;
646
- hasCustomHooks = typeof getCustomHooks === 'function' ;
647
- setSignature ( type , key , forceReset , getCustomHooks ) ;
648
- // The next call we expect is from inside a function, to fill in the custom Hooks.
649
- status = 'needsCustomHooks' ;
650
- }
651
- break ;
652
- case 'needsCustomHooks' :
653
- if ( hasCustomHooks ) {
654
- collectCustomHooksForSignature ( savedType ) ;
655
- }
656
- status = 'resolved' ;
657
- break ;
658
- case 'resolved' :
659
- // Do nothing. Fast path for all future renders.
660
- break ;
648
+ ) : T | void {
649
+ if ( typeof key === 'string' ) {
650
+ // We're in the initial phase that associates signatures
651
+ // with the functions. Note this may be called multiple times
652
+ // in HOC chains like _s(hoc1(_s(hoc2(_s(actualFunction))))).
653
+ if ( ! savedType ) {
654
+ // We're in the innermost call, so this is the actual type.
655
+ savedType = type ;
656
+ hasCustomHooks = typeof getCustomHooks === 'function' ;
657
+ }
658
+ // Set the signature for all types (even wrappers!) in case
659
+ // they have no signatures of their own. This is to prevent
660
+ // problems like https://github.com/facebook/react/issues/20417.
661
+ if (
662
+ type != null &&
663
+ ( typeof type === 'function' || typeof type === 'object' )
664
+ ) {
665
+ setSignature ( type , key , forceReset , getCustomHooks ) ;
666
+ }
667
+ return type ;
668
+ } else {
669
+ // We're in the _s() call without arguments, which means
670
+ // this is the time to collect custom Hook signatures.
671
+ // Only do this once. This path is hot and runs *inside* every render!
672
+ if ( ! didCollectHooks && hasCustomHooks ) {
673
+ didCollectHooks = true ;
674
+ collectCustomHooksForSignature ( savedType ) ;
675
+ }
661
676
}
662
- return type ;
663
677
} ;
664
678
} else {
665
679
throw new Error (
0 commit comments