import { EffectModule, ActionOfEffectModule } from '@sigi/core' import { ConstructorOf, IStore } from '@sigi/types' import { useDebugValue } from 'react' import { identity, skip } from 'rxjs' import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector' import { useInstance } from './injectable-context' import { shallowEqual } from './shallow-equal' export type StateSelector = { (state: S): U } export type StateSelectorConfig = { selector: (state: S) => U equalFn?: (u1: U, u2: U) => boolean // use never to tell user that it is not required any more dependencies?: never } export function useDispatchers, S = any>(A: ConstructorOf): ActionOfEffectModule { const effectModule = useInstance(A) return effectModule.dispatchers } function _useModuleState( store: IStore, // @ts-expect-error valid assignment selector: StateSelectorConfig['selector'] = identity, equalFn = shallowEqual, ): S | U { const state = useSyncExternalStoreWithSelector( (onStoreChange) => { const sub = store.state$.pipe(skip(1)).subscribe(onStoreChange) return () => sub.unsubscribe() }, () => store.state, () => store.state, selector, equalFn, ) useDebugValue(state) return state } export function useModuleState>( A: ConstructorOf, ): M extends EffectModule ? State : never export function useModuleState, U>( A: ConstructorOf, config: M extends EffectModule ? StateSelectorConfig : never, ): M extends EffectModule ? (typeof config)['selector'] extends StateSelector ? NewState : never : never export function useModuleState, U>( A: ConstructorOf, config?: M extends EffectModule ? StateSelectorConfig : never, ) { const { store } = useInstance(A) return _useModuleState(store, config?.selector, config?.equalFn) } export function useModule>( A: ConstructorOf, ): M extends EffectModule ? [State, ActionOfEffectModule] : never export function useModule, U>( A: ConstructorOf, config: M extends EffectModule ? StateSelectorConfig : never, ): M extends EffectModule ? (typeof config)['selector'] extends StateSelector ? [NewState, ActionOfEffectModule] : never : never export function useModule, U, S>(A: ConstructorOf, config?: StateSelectorConfig) { const effectModule = useInstance(A) const { store } = effectModule const appState = _useModuleState(store, config?.selector) const appDispatcher = effectModule.dispatchers return [appState, appDispatcher] } export { SSRContext } from './ssr-context' export * from './injectable-context'