@@ -612,57 +612,53 @@ export function _getMountedRootCount() {
612612// function Hello() {
613613// const [foo, setFoo] = useState(0);
614614// const value = useCustomHook();
615- // _s(); /* Second call triggers collecting the custom Hook list.
615+ // _s(); /* Call without arguments triggers collecting the custom Hook list.
616616// * This doesn't happen during the module evaluation because we
617617// * don't want to change the module order with inline requires.
618618// * Next calls are noops. */
619619// return <h1>Hi</h1>;
620620// }
621621//
622- // /* First call specifies the signature: */
622+ // /* Call with arguments attaches the signature to the type : */
623623// _s(
624624// Hello,
625625// 'useState{[foo, setFoo]}(0)',
626626// () => [useCustomHook], /* Lazy to avoid triggering inline requires */
627627// );
628- type SignatureStatus = 'needsSignature ' | 'needsCustomHooks ' | 'resolved ';
629628export function createSignatureFunctionForTransform ( ) {
630629 if ( __DEV__ ) {
631- // We'll fill in the signature in two steps.
632- // First, we'll know the signature itself. This happens outside the component.
633- // Then, we'll know the references to custom Hooks. This happens inside the component.
634- // After that, the returned function will be a fast path no-op.
635- let status : SignatureStatus = 'needsSignature' ;
636630 let savedType ;
637631 let hasCustomHooks ;
632+ let didCollectHooks = false ;
638633 return function < T > (
639634 type: T,
640635 key: string,
641636 forceReset?: boolean,
642637 getCustomHooks?: () => Array < Function > ,
643638 ) : T {
644- switch ( status ) {
645- case 'needsSignature' :
646- if ( type !== undefined ) {
647- // If we received an argument, this is the initial registration call.
648- savedType = type ;
649- hasCustomHooks = typeof getCustomHooks === 'function' ;
650- setSignature ( type , key , forceReset , getCustomHooks ) ;
651- // The next call we expect is from inside a function, to fill in the custom Hooks.
652- status = 'needsCustomHooks' ;
653- }
654- break ;
655- case 'needsCustomHooks' :
656- if ( hasCustomHooks ) {
657- collectCustomHooksForSignature ( savedType ) ;
658- }
659- status = 'resolved' ;
660- break ;
661- case 'resolved' :
662- // Do nothing. Fast path for all future renders.
663- break ;
639+ if ( typeof key === 'string' ) {
640+ // We're in the initial phase that associates signatures
641+ // with the functions. Note this may be called multiple times
642+ // in HOC chains like _s(hoc1(_s(hoc2(_s(actualFunction))))).
643+ if ( ! savedType ) {
644+ // We're in the innermost call, so this is the actual type.
645+ savedType = type ;
646+ hasCustomHooks = typeof getCustomHooks === 'function' ;
647+ }
648+ // Set the signature for all types (even wrappers!) in case
649+ // they have no signatures of their own. This is to prevent
650+ // problems like https://github.com/facebook/react/issues/20417.
651+ setSignature ( type , key , forceReset , getCustomHooks ) ;
652+ return type ;
653+ } else {
654+ // We're in the _s() call without arguments, which means
655+ // this is the time to collect custom Hook signatures.
656+ // Only do this once. This path is hot and runs *inside* every render!
657+ if ( ! didCollectHooks && hasCustomHooks ) {
658+ didCollectHooks = true ;
659+ collectCustomHooksForSignature ( savedType ) ;
660+ }
664661 }
665- return type ;
666662 } ;
667663 } else {
668664 throw new Error (
0 commit comments