import * as React from 'react' ; import { call , getCurrent , observable } from '@goldfishjs/reactive' ; import { isObject , cloneDeep } from '@goldfishjs/utils' ; import ComponentSetup from './ComponentSetup' ; import { setupManager } from './ComponentSetupManager' ; export interface ISetupFunction < R extends Record < string , any > > { ( ) : R ; } export type ReactLike = Pick < typeof React , 'useState' | 'useMemo' | 'useEffect' | 'useRef' | 'useCallback' > ; export default function observer < P , T extends React . FunctionComponent < P > = React . FunctionComponent < P > > ( reactLike , componentFn ) ; export default function observer < P , T extends React . FunctionComponent < P > = React . FunctionComponent < P > > ( reactLike , defaultProps , componentFn ) ; export default function observer < P , SR extends Record < string , any > = Record < string , any > , T extends React . FunctionComponent < P > = React . FunctionComponent < P > > ( reactLike , componentFn , setupFn ) ; export default function observer < P , SR extends Record < string , any > = Record < string , any > , T extends React . FunctionComponent < P > = React . FunctionComponent < P > > ( reactLike , defaultProps , componentFn , setupFn ) ; export default function observer < P , SR extends Record < string , any > , T extends React . FunctionComponent < P > > ( reactLike , passInDefaultProps , passInComponentFn ? , passInSetupFn ? ) { let defaultProps = { } ; let componentFn ; let setupFn = undefined ; if ( ! passInComponentFn && ! passInSetupFn ) { componentFn = passInDefaultProps as Function ; } else if ( ! passInSetupFn ) { defaultProps = typeof passInDefaultProps === 'function' ? { } : passInDefaultProps ; componentFn = ( typeof passInDefaultProps === 'function' ? passInDefaultProps : passInComponentFn ) as Function ; setupFn = typeof passInDefaultProps === 'function' ? ( passInComponentFn as ISetupFunction < SR > ) : undefined ; } else { defaultProps = passInDefaultProps ; componentFn = passInComponentFn as Function ; setupFn = passInSetupFn ; } const fn = function ( this , props , context ? ) { const [ counter , setCounter ] = reactLike . useState ( 0 ) ; const [ id ] = reactLike . useState ( counter === 0 ? setupManager . genId ( ) : '' ) ; const isFirstTime = reactLike . useRef < boolean > ( true ) ; if ( isFirstTime . current ) { isFirstTime . current = false ; const setup = new ComponentSetup ( ) ; setupManager . add ( id , setup ) ; if ( setupFn ) { setup . props = observable ( cloneDeep ( defaultProps ) ) ; setup . setupFnResult = setup . wrap ( setupFn ) ; } } reactLike . useMemo ( ( ) => { if ( ! setupFn ) { return ; } const setup = setupManager . get ( id ) ; if ( isObject ( props ) ) { for ( const key in props ) { if ( ! ( key in setup . props ) ) { console . warn ( `The key: ${ key } will not be reactive, because it is not declared in defaultProps: ${ JSON . stringify ( defaultProps ) }` ) ; } if ( props [ key ] !== setup . props [ key ] ) { setup . props [ key ] = props [ key ] ; } } } } , [ props ] ) ; reactLike . useEffect ( ( ) => { const setup = setupManager . get ( id ) ; setup . initData . init ( ) ; } , [ ] ) ; reactLike . useEffect ( ( ) => { const setup = setupManager . get ( id ) ; setup . mountFns . forEach ( fn => fn ( ) ) ; setup . mountFns = [ ] ; return ( ) => { setup . removeAllStopList ( ) ; setup . stopAllAutorun ( ) ; setup . stopAllWatch ( ) ; setup . unmountFns . forEach ( fn => fn ( ) ) ; setup . unmountFns = [ ] ; } ; } , [ ] ) ; let result = null ; call ( ( ) => { const setup = setupManager . get ( id ) ; setup . removeAllStopList ( ) ; result = setup . setupFnResult ? componentFn . call ( this , setup . setupFnResult , props , context ) : componentFn . call ( this , props , context ) ; const list = getCurrent ( ) . addChangeListener ( ( ) => { setCounter ( counter + 1 ) ; } , false ) ; setup . addStopList ( list ) ; } ) ; return result ; } ; return fn as T ; }