import { useReducer , useRef , useEffect , useLayoutEffect , Dispatch , } from 'react' ; import { Subject , Operator , makeSubject , subscribe , pipe } from 'wonka' ; import { CallbackNode , unstable_scheduleCallback as scheduleCallback , unstable_cancelCallback as cancelCallback , unstable_getCurrentPriorityLevel as getPriorityLevel , } from 'scheduler' ; type TeardownFn = ( ) => void ; interface State < R , T = R > { subject : Subject < T > ; onValue : Dispatch < R > ; teardown : null | TeardownFn ; task : null | CallbackNode ; value : R ; } const isServerSide = typeof window === 'undefined' ; const useIsomorphicEffect = ! isServerSide ? useLayoutEffect : useEffect ; export const useOperatorValue = < T , R > ( fn , input , init ) : R => useOperator < T , R > ( fn , input , init ) [ 0 ] ; /**
 * Creates a stream of `input` as it's changing and pipes this stream
 * into the operator which creates what becomes the output of this hook.
 *
 * This hooks supports creating a synchronous, stateful value that is
 * updated immediately on mount and is then updated using normal effects.
 * It has been built to be safe for normal-mode, concurrent-mode,
 * strict-mode and suspense.
 */ export const useOperator = < T , R > ( operator , input , init ? ) : [ R , Dispatch < T > ] => { const subscription = useRef < State < R , T > > ( { subject : makeSubject < T > ( ) , value : init as R , onValue : ( value ) => { subscription . current . value = value ; } , teardown : null , task : null , } ) ; const [ , setValue ] = useReducer ( ( x , value ) => { subscription . current . value = value ; return x + 1 ; } , 0 ) ; if ( subscription . current . teardown === null ) { const { unsubscribe } = pipe ( operator ( subscription . current . subject . source ) , subscribe ( ( value ) => subscription . current . onValue ( value ) ) ) ; subscription . current . subject . next ( input ) ; if ( isServerSide ) { unsubscribe ( ) ; } else { subscription . current . teardown = unsubscribe as TeardownFn ; subscription . current . task = scheduleCallback ( getPriorityLevel ( ) , subscription . current . teardown ) ; } } useIsomorphicEffect ( ( ) => { if ( subscription . current . task !== null ) { cancelCallback ( subscription . current . task ) ; } return ( ) => { if ( subscription . current . teardown !== null ) { subscription . current . teardown ( ) ; } } ; } , [ ] ) ; useEffect ( ( ) => { const isInitial = subscription . current . onValue !== setValue ; subscription . current . onValue = setValue ; if ( subscription . current . teardown === null ) { subscription . current . teardown = pipe ( operator ( subscription . current . subject . source ) , subscribe ( setValue ) ) . unsubscribe as TeardownFn ; } if ( ! isInitial ) { subscription . current . subject . next ( input ) ; } } , [ input ] ) ; return [ subscription . current . value , subscription . current . subject . next ] ; } ;