@@ -355,12 +355,25 @@ export function setSignature(
355355 getCustomHooks ?: ( ) => Array < Function > ,
356356): void {
357357 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+ }
364377 } else {
365378 throw new Error (
366379 'Unexpected call to React Refresh in a production environment.' ,
@@ -609,57 +622,58 @@ export function _getMountedRootCount() {
609622// function Hello() {
610623// const [foo, setFoo] = useState(0);
611624// const value = useCustomHook();
612- // _s(); /* Second call triggers collecting the custom Hook list.
625+ // _s(); /* Call without arguments triggers collecting the custom Hook list.
613626// * This doesn't happen during the module evaluation because we
614627// * don't want to change the module order with inline requires.
615628// * Next calls are noops. */
616629// return <h1>Hi</h1>;
617630// }
618631//
619- // /* First call specifies the signature: */
632+ // /* Call with arguments attaches the signature to the type : */
620633// _s(
621634// Hello,
622635// 'useState{[foo, setFoo]}(0)',
623636// () => [useCustomHook], /* Lazy to avoid triggering inline requires */
624637// );
625- type SignatureStatus = 'needsSignature ' | 'needsCustomHooks ' | 'resolved ';
626638export function createSignatureFunctionForTransform ( ) {
627639 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' ;
633640 let savedType ;
634641 let hasCustomHooks ;
642+ let didCollectHooks = false ;
635643 return function < T > (
636644 type: T,
637645 key: string,
638646 forceReset?: boolean,
639647 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+ }
661676 }
662- return type ;
663677 } ;
664678 } else {
665679 throw new Error (
0 commit comments