import shallow from './shallow' ; import { AsyncAction , SimpleSyncAction , SyncAction , StoreAction , AsyncActionPromise , ConflictPolicy , AsyncActionPromiseGet , PromiseProvider , GetPromiseInvocationContext , DecoratedActions , InternalLoadingMap , StoreOptions , PromiseResult , InvocationContext , EffectsInvocationContext , InvocationContextActions , WarningNotifyFunc , ErrorEffectsInvocationContext , OnMountInvocationContext , OnPropsChangeEffectsContext , ContextState , Middleware , MiddlewareFactory , OnPropsChangeOptions , ContextWithDerived , ContextDerivedStateState , } from './types' ; export type SetStateFunc < S , A , P > = ( newState : S , newLoadingMap : InternalLoadingMap < A > , actionName : keyof A | 'onPropsChange' , actionType : 'preEffects' | 'effects' | 'errorEffects' | null , isAsync : boolean , actionCtx : any , propsChanged : boolean , isInit ? : boolean ) => void ; internal export type PromiseMap < A > = { [ name in keyof A ] ? : { [ promiseId ] : { promise : Promise < any > ; refArgs : any [ ] ; abortController : AbortController ; } ; } ; } ; internal export type FutureActions = { args : any [ ] ; resolve : ( ... args : any [ ] ) => void ; reject : ( ... args : any [ ] ) => void ; timestamp ? : number ; } ; internal export type ConflictActionsMap < A > = { [ name in keyof A ] ? : FutureActions [ ] ; } ; internal export type AsyncActionExecContext < S , DS , F extends ( ... args : any [ ] ) => any , A extends DecoratedActions , P > = { actionName : keyof A ; action : AsyncActionPromise < S , F , A , P , DS > ; stateRef : Ref < S > ; derivedStateRef : Ref < DerivedState < DS > > ; propsRef : Ref < P > ; loadingMapRef : Ref < InternalLoadingMap < A > > ; promisesRef : Ref < PromiseMap < A > > ; conflictActionsRef : Ref < ConflictActionsMap < A > > ; actionsRef : Ref < A > ; initializedRef : Ref < boolean > ; options : StoreOptions < S , A , P , any > ; setState : SetStateFunc < S , A , P > ; } ; internal export type TimeoutMap < A > = { [ name in keyof A ] ? : any ; } ; export type NotifyFunc = ( msg : string ) => void ; export type CloneFunction = < C > ( obj : C ) => C ; export type ComparatorFunction = < C > ( obj1 : C , Obj2 : C ) => boolean ; export type RetryOnErrorFunction = ( error : any ) => boolean ; export type AsyncErrorHandler = ( error : any , isHandled : boolean , state : any , props : any , actionName : string , args : any [ ] ) => void ; type DerivedStateMap < DS > = { [ name in keyof DS ] ? : any [ ] ; } ; internal export type DerivedState < DS > = { state : DS ; deps : DerivedStateMap < DS > ; } ; /**
 * Global configuration, shared with all stores.
 */ export type GlobalConfig = { default clone : CloneFunction ; default comparator : ComparatorFunction ; /**
     * Global callback function to handle asychronous actions rejected promise.
     */ asyncErrorHandler : AsyncErrorHandler ; see AsynchActionBase retryOnErrorFunction : RetryOnErrorFunction ; /**
     * Global notification function that will called when an asynchronous action succeeded,
     * if and only if, an error message is specified for this action using
     * <code>getErrorMessage</code>.
     * If another function is set in the options, the function from the options will be used.
     */ notifySuccess : NotifyFunc ; /**
     * Global notification function that will called when an asynchronous action fails,
     * if and only if, an error message is specified for this action using
     * <code>getErrorMessage</code>.
     * If another function is set in the options, the function from the options will be used.
     */ notifyError : NotifyFunc ; /**
     * Global notification function that will injected in onDone and onFail of an asynchronous actions and onActionDone of a synchronous function,
     * If another function is set in the options, the function from the options will be used.
     */ notifyWarning : NotifyFunc ; /**
     * A default error message provider.
     * Used to return generic messages on generic HTTP errors
     */ getErrorMessage : ( error : Error ) => string ; /**
     * Default middlewares. Store middleware array will be concatenated with this one.
     * By default contains only the optimistic middleware.
     */ defaultMiddlewares : MiddlewareFactory < any , any , any > [ ] ; } ; internal export const globalConfig = { clone : defaultCloneFunc , comparator : shallow , asyncErrorHandler : ( ) => { } , retryOnErrorFunction : ( error ) => error instanceof TypeError , notifySuccess : undefined , notifyError : undefined , notifyWarning : undefined , getErrorMessage : undefined , defaultMiddlewares : [ ] , } ; export function setGlobalConfig ( overrideConfig ) { const keys = Object . keys ( overrideConfig ) as ( keyof GlobalConfig ) [ ] ; keys . forEach ( ( k ) => { if ( overrideConfig [ k ] != null ) { globalConfig [ k ] = overrideConfig [ k ] as any ; } } ) ; } function defaultCloneFunc ( src ) { return JSON . parse ( JSON . stringify ( src ) ) ; } function clone ( obj ) { try { return globalConfig . clone ( obj ) ; } catch ( e ) { const msg = 'StateDecorator: Cannot clone object. Call setCloneFunction with another implementation like lodash/cloneDeep.' ; if ( process . env . NODE_ENV === 'development' ) { console . error ( msg ) ; console . error ( e . toString ( ) ) ; } throw new Error ( msg ) ; } } /**
 * Type guard function to test if an action is a asynchronous action.
 */ export function isAsyncAction < S , DS , F extends ( ... args : any [ ] ) => any , A , P , FxRes = S > ( action ) { return ( ! ( action instanceof Function ) && ( action . hasOwnProperty ( 'getPromise' ) || action . hasOwnProperty ( 'getGetPromise' ) ) ) ; } /**
 * Type guard function to test if an action is a synchronous action.
 */ export function isSimpleSyncAction < S , F extends ( ... args : any [ ] ) => any , A , P , DS = { } , FxRes = S > ( action ) { return typeof action === 'function' ; } /**
 * Type guard function to test if an action is a synchronous action.
 */ export function isSyncAction < S , F extends ( ... args : any [ ] ) => any , A , P , DS = { } , FxRes = S > ( action ) { return ( typeof action !== 'function' && ! action . hasOwnProperty ( 'getPromise' ) && ! action . hasOwnProperty ( 'getGetPromise' ) ) ; } export function isAsyncGetPromiseAction < S , DS , F extends ( ... args : any [ ] ) => any , A , P , FxRes = S > ( action ) { return typeof action !== 'function' && action . hasOwnProperty ( 'getPromise' ) && ! action . hasOwnProperty ( 'getGetPromise' ) ; } export function isAsyncGetPromiseGetAction < S , DS , F extends ( ... args : any [ ] ) => any , A , P , FxRes = S > ( action ) { return typeof action !== 'function' && ! action . hasOwnProperty ( 'getPromise' ) && action . hasOwnProperty ( 'getGetPromise' ) ; } internal export function computeAsyncActionInput < S , DS , F extends ( ... args : any [ ] ) => any , A , P , FxRes = S > ( action ) { if ( isAsyncGetPromiseGetAction ( action ) ) { return { ... ( action as any ) , getPromise : action . getGetPromise , retryCount : 3 , conflictPolicy : ConflictPolicy . REUSE , } ; } return action ; } internal export function buildLoadingMap < A > ( source , actionName , promiseId , loading ) { if ( loading ) { return { ... source , [ actionName ] : { ... ( source [ actionName ] || { } ) , [ promiseId ] : loading } , } ; } const res = { ... source , [ actionName ] : { ... source [ actionName ] , [ promiseId ] : false } , } ; return res ; } internal export function retryPromiseDecorator < S , DS , F extends ( ... args : any [ ] ) => Promise < any > , A , P > ( promiseProvider , maxCalls = 1 , delay = 1000 , isRetryError = ( ) => true ) { if ( maxCalls === 1 ) { return promiseProvider ; } return ( ctx ) : ReturnType < F > => { async function call ( callCount , p ) { if ( p === null ) { return null ; } return p . catch ( ( e ) => { if ( isRetryError ( e ) ) { if ( callCount === maxCalls ) { return Promise . reject ( e ) ; } return new Promise ( ( resolve , reject ) => { setTimeout ( ( ) => { const p = promiseProvider ( ctx ) ; call ( callCount + 1 , p ) . then ( resolve ) . catch ( reject ) ; } , delay * callCount ) ; } ) ; } return Promise . reject ( e ) ; } ) ; } const initialPromise = promiseProvider ( ctx ) ; if ( initialPromise === null ) { return null ; } return call ( 1 , initialPromise ) as ReturnType < F > ; } ; } export type Ref < P > = { current : P ; } ; internal export function createRef < U > ( init = null ) { return { current : init , } ; } internal export function buildInvocationContextBase < S , DS , P > ( stateRef , derivedStateRef , propsRef ) { return { state : stateRef . current , s : stateRef . current , ds : derivedStateRef ?. current ?. state , derived : derivedStateRef ?. current ?. state , props : propsRef . current , p : propsRef . current , } ; } internal export function buildOnMountInvocationContext < S , DS , A , P > ( stateRef , derivedStateRef , propsRef , actionsRef ) { return addContextActions ( buildInvocationContextBase ( stateRef , derivedStateRef , propsRef ) , actionsRef ) ; } internal export function buildOnPropChangeEffects < S , DS , P > ( stateRef , derivedStateRef , propsRef , indices , index , isInit ) { const res = buildInvocationContextBase ( stateRef , derivedStateRef , propsRef ) as OnPropsChangeEffectsContext < S , DS , P > ; res . indices = indices ; res . index = index ; res . isInit = isInit ; return res ; } internal function buildInvocationContext < S , DS , F extends ( ... args : any [ ] ) => any , P > ( stateRef , derivedStateRef , propsRef , args , promiseId ? ) { const res = buildInvocationContextBase ( stateRef , derivedStateRef , propsRef ) as InvocationContext < S , DS , F , P > ; res . args = args ; if ( promiseId ) { res . promiseId = promiseId ; } return res ; } internal function buildEffectsInvocationContext < S , DS , F extends ( ... args : any [ ] ) => any , P > ( stateRef , derivedStateRef , propsRef , args , result , promiseId ? ) { const res = buildInvocationContext ( stateRef , derivedStateRef , propsRef , args , promiseId ) as EffectsInvocationContext < S , DS , F , P > ; res . result = result ; res . res = result ; return res ; } internal function buildErrorEffectsInvocationContext < S , DS , F extends ( ... args : any [ ] ) => any , P > ( stateRef , derivedStateRef , propsRef , args , error , promiseId ? ) { const res = buildInvocationContext ( stateRef , derivedStateRef , propsRef , args , promiseId ) as ErrorEffectsInvocationContext < S , DS , F , P > ; res . error = error ; res . err = error ; return res ; } internal function buildPromiseActionContext < S , DS , F extends ( ... args : any [ ] ) => any , P , A > ( stateRef , derivedStateRef , propsRef , args , actionsRef , abortSignal , promiseId ? ) { const res = addContextActions ( buildInvocationContext ( stateRef , derivedStateRef , propsRef , args , promiseId ) , actionsRef ) as GetPromiseInvocationContext < S , DS , F , P , A > ; res . abortSignal = abortSignal ; return res ; } internal export function addStateToContext < T , S > ( ctx , stateRef ) { const newCtx = ctx as T & ContextState < S > ; newCtx . state = stateRef . current ; newCtx . s = stateRef . current ; return newCtx ; } internal export function addContextActions < T , A > ( ctx , actionsRef ) { const newCtx = ctx as T & InvocationContextActions < A > ; newCtx . actions = actionsRef . current ; newCtx . a = actionsRef . current ; return newCtx ; } internal function addSideEffectsContext < S , DS , T extends { s : S ; state : S ; } , A > ( ctx , stateRef , derivedStateRef , actionsRef , notifyWarning ) { const res = addContextActions ( ctx , actionsRef ) as T & InvocationContextActions < A > & WarningNotifyFunc & ContextDerivedStateState < DS > ; addStateToContext ( ctx , stateRef ) ; const ds = derivedStateRef ?. current ?. state ; res . derived = ds ; res . ds = ds ; res . notifyWarning = notifyWarning ; return res ; } function notInitWarning < A > ( actionName ) { if ( process . env . NODE_ENV === 'development' ) { console . warn ( `[state-decorator] ${ actionName } action was called while store is not initialized or destroyed, this is probably a leak` ) ; } } internal export function decorateSimpleSyncAction < S , DS , F extends ( ... args : any [ ] ) => any , A extends DecoratedActions , P > ( actionName , action , stateRef , derivedStateRef , propsRef , initializedRef , setState ) { return ( ... args ) => { if ( ! initializedRef . current ) { notInitWarning ( actionName ) ; return ; } const ctx = buildInvocationContext ( stateRef , derivedStateRef , propsRef , args ) ; const newState = action ( ctx ) ; setState ( newState , null , actionName , 'effects' , false , ctx , false ) ; } ; } internal function executeSyncActionImpl < S , DS , F extends ( ... args : any [ ] ) => any , A extends DecoratedActions , P > ( actionName , action , stateRef , derivedStateRef , propsRef , actionsRef , timeoutMap , options , setState , args ) { const ctx = buildEffectsInvocationContext ( stateRef , derivedStateRef , propsRef , args , undefined ) ; let actionDropped = false ; if ( action . effects != null ) { const newState = action . effects ( ctx ) ; if ( newState === null ) { actionDropped = true ; } else { setState ( newState , undefined , actionName , 'effects' , false , ctx , false ) ; } } if ( ! actionDropped ) { if ( action . debounceSideEffectsTimeout > 0 ) { const timeout = timeoutMap . current [ actionName ] ; if ( timeout ) { clearTimeout ( timeout ) ; } timeoutMap . current [ actionName ] = setTimeout ( ( ) => { action . sideEffects ( addSideEffectsContext ( ctx , stateRef , derivedStateRef , actionsRef , options . notifyWarning ) ) ; delete timeoutMap . current [ actionName ] ; } , action . debounceSideEffectsTimeout ) ; } else if ( action . sideEffects ) { action . sideEffects ( addSideEffectsContext ( ctx , stateRef , derivedStateRef , actionsRef , options . notifyWarning ) ) ; } } } internal export function decorateSyncAction < S , DS , F extends ( ... args : any [ ] ) => any , A extends DecoratedActions , P > ( actionName , action , stateRef , derivedStateRef , propsRef , actionsRef , initializedRef , timeoutMap , options , setState ) { return ( ... args ) => { if ( ! initializedRef . current ) { notInitWarning ( actionName ) ; return ; } if ( action . debounceTimeout ) { const timeout = timeoutMap . current [ actionName ] ; if ( timeout ) { clearTimeout ( timeout ) ; } timeoutMap . current [ actionName ] = setTimeout ( ( ) => { delete timeoutMap . current [ actionName ] ; executeSyncActionImpl ( actionName , action , stateRef , derivedStateRef , propsRef , actionsRef , timeoutMap , options , setState , args ) ; } , action . debounceTimeout ) ; } else { executeSyncActionImpl ( actionName , action , stateRef , derivedStateRef , propsRef , actionsRef , timeoutMap , options , setState , args ) ; } } ; } function processPromiseSuccess < S , DS , F extends ( ... args : any [ ] ) => any , A extends DecoratedActions , P > ( context , promiseId , promiseResult , args ) { const { action , stateRef , derivedStateRef , propsRef , promisesRef , actionsRef , loadingMapRef , conflictActionsRef , actionName , options , setState , } = context ; if ( ! context . initializedRef . current ) { return void 0 ; } let newState = null ; const ctx = buildEffectsInvocationContext ( stateRef , derivedStateRef , propsRef , args , promiseResult , promiseId ) ; if ( action . effects ) { newState = action . effects ( ctx ) ; } setState ( newState , buildLoadingMap ( loadingMapRef . current , actionName , promiseId , false ) , actionName , 'effects' , true , ctx , false ) ; const notifySuccess = options . notifySuccess || globalConfig . notifySuccess ; if ( notifySuccess ) { let msg ; if ( action . getSuccessMessage ) { msg = action . getSuccessMessage ( ctx ) ; } if ( msg ) { notifySuccess ( msg ) ; } } delete promisesRef . current [ actionName ] [ promiseId ] ; if ( action . sideEffects ) { action . sideEffects ( addSideEffectsContext ( ctx , stateRef , derivedStateRef , actionsRef , globalConfig . notifyWarning ) ) ; } processNextConflictAction ( actionName , actionsRef . current , conflictActionsRef . current ) ; return promiseResult ; } function processPromiseFailed < S , DS , F extends ( ... args : any [ ] ) => any , A extends DecoratedActions , P > ( context , promiseId , error , args ) { const { action , stateRef , derivedStateRef , propsRef , actionsRef , loadingMapRef , promisesRef , conflictActionsRef , actionName , options , setState , } = context ; if ( ! context . initializedRef . current ) { return void 0 ; } let newState = null ; const ctx = buildErrorEffectsInvocationContext ( stateRef , derivedStateRef , propsRef , args , error , promiseId ) ; if ( action . errorEffects ) { newState = action . errorEffects ( ctx ) ; } setState ( newState , buildLoadingMap ( loadingMapRef . current , actionName , promiseId , false ) , actionName , 'errorEffects' , true , ctx , false ) ; let errorHandled = false ; if ( newState != null || action . errorSideEffects ) { errorHandled = true ; } const notifyError = options . notifyError || globalConfig . notifyError ; if ( notifyError ) { let msg ; if ( action . getErrorMessage ) { msg = action . getErrorMessage ( ctx ) ; } if ( msg === undefined && ! errorHandled && globalConfig . getErrorMessage ) { msg = globalConfig . getErrorMessage ( error ) ; } if ( msg ) { errorHandled = true ; notifyError ( msg ) ; } } globalConfig . asyncErrorHandler ( error , errorHandled || action . rejectPromiseOnError , stateRef . current , propsRef . current , actionName as string , args ) ; delete promisesRef . current [ actionName ] [ promiseId ] ; if ( action . errorSideEffects ) { action . errorSideEffects ( addSideEffectsContext ( ctx , stateRef , derivedStateRef , actionsRef , globalConfig . notifyWarning ) ) ; } processNextConflictAction ( actionName , actionsRef . current , conflictActionsRef . current ) ; const result = ! errorHandled || action . rejectPromiseOnError ? Promise . reject ( error ) : Promise . resolve ( ) ; return result ; } internal export function decorateAsyncAction < S , DS , F extends ( ... args : any [ ] ) => any , A extends DecoratedActions , P > ( context ) { const { action , stateRef , derivedStateRef , propsRef , actionsRef , promisesRef , loadingMapRef , initializedRef , conflictActionsRef , actionName , setState , } = context ; return ( ... args ) : Promise < any > => { if ( ! initializedRef . current ) { notInitWarning ( actionName ) ; return null ; } const promises = promisesRef . current ; const { conflictPolicy = ConflictPolicy . KEEP_ALL } = action ; const isParallel = conflictPolicy === ConflictPolicy . PARALLEL ; let promiseId = null ; if ( isParallel ) { if ( ! action . getPromiseId ) { throw new Error ( 'If conflict policy is set to ConflictPolicy.PARALLEL, getPromiseId must be set and return a string.' ) ; } promiseId = action . getPromiseId ( ... args ) ; } else { promiseId = DEFAULT_PROMISE_ID ; } if ( ! isParallel && promises [ actionName ] ?. [ promiseId ] ) { return handleConflictingAction ( promises , conflictActionsRef . current , actionName , conflictPolicy , args ) ; } let newState = null ; let hasChanged = false ; const ctx = buildInvocationContext ( stateRef , derivedStateRef , propsRef , args , promiseId ) ; if ( action . preEffects ) { newState = action . preEffects ( ctx ) ; if ( newState ) { hasChanged = true ; stateRef . current = newState ; } } const { abortable , retryCount , retryDelaySeed , isTriggerRetryError } = action ; const abortController = abortable && window . AbortController ? new AbortController ( ) : null ; const promiseProvider = retryPromiseDecorator ( action . getPromise , retryCount ? 1 + retryCount : 1 , retryDelaySeed , isTriggerRetryError || globalConfig . retryOnErrorFunction ) ; const p = promiseProvider ( buildPromiseActionContext ( stateRef , derivedStateRef , propsRef , args , actionsRef , abortController ?. signal ) ) ?. then ( ( promiseResult ) => processPromiseSuccess ( context , promiseId , promiseResult , args ) ) . catch ( ( error ) => processPromiseFailed ( context , promiseId , error , args ) ) ; if ( p != null ) { promises [ actionName ] = { ... ( promises [ actionName ] || { } ) , [ promiseId || DEFAULT_PROMISE_ID ] : { abortController , promise : p , refArgs : conflictPolicy === ConflictPolicy . REUSE && args . length > 0 ? [ ... args ] : [ ] , } , } ; } setState ( p == null && ! hasChanged ? undefined : stateRef . current , p == null ? undefined : buildLoadingMap ( loadingMapRef . current , actionName , promiseId , true ) , actionName , 'preEffects' , true , ctx , false ) ; return p ; } ; } internal export function isLoadingImpl < A , K extends keyof A > ( loadingMap , ... props ) { return props . some ( ( propIn ) => { let actionName = null ; let promiseId = DEFAULT_PROMISE_ID ; if ( Array . isArray ( propIn ) ) { [ actionName , promiseId = DEFAULT_PROMISE_ID ] = propIn ; } else { actionName = propIn ; } const res = loadingMap [ actionName ] ; if ( res == null ) { return false ; } if ( res [ promiseId ] != null ) { return res [ promiseId ] ; } return false ; } ) ; } export const DEFAULT_PROMISE_ID = '__def__' ; internal export function areSameArgs ( args1 , args2 ) { if ( args1 . length !== args2 . length ) { return false ; } return args1 . findIndex ( ( value , index ) => args2 [ index ] !== value ) === - 1 ; } /**
 * Handles a conflicting action depending on the action conflict policy.
 */ internal function handleConflictingAction < A > ( promises , conflictActions , actionName , conflictPolicy , args ) { let policy = conflictPolicy ; if ( policy === ConflictPolicy . REUSE ) { if ( areSameArgs ( promises [ actionName ] [ DEFAULT_PROMISE_ID ] . refArgs , args ) ) { return promises [ actionName ] [ DEFAULT_PROMISE_ID ] . promise ; } policy = ConflictPolicy . KEEP_ALL ; } return new Promise ( ( resolve , reject ) => { const futureAction = { resolve , reject , args : clone ( args ) , timestamp : Date . now ( ) , } ; switch ( policy ) { case ConflictPolicy . IGNORE : resolve ( void 0 ) ; break ; case ConflictPolicy . REJECT : reject ( new Error ( `An asynchronous action ${ actionName } is already ongoing.` ) ) ; break ; case ConflictPolicy . KEEP_LAST : { conflictActions [ actionName ] = [ futureAction ] ; break ; } case ConflictPolicy . KEEP_ALL : { let stack = conflictActions [ actionName ] ; if ( ! stack ) { conflictActions [ actionName ] = stack = [ ] ; } stack . push ( futureAction ) ; break ; } case ConflictPolicy . PARALLEL : case ConflictPolicy . REUSE : break ; default : const exhaustiveCheck = policy ; exhaustiveCheck ; break ; } } ) ; } /**
 * Processes next conflicting action (see ConflictPolicy) in the queue.
 */ function processNextConflictAction < A > ( actionName , actions , conflictActions ) { const futureActions = conflictActions [ actionName ] ; if ( futureActions && futureActions . length > 0 ) { const futureAction = conflictActions [ actionName ] . shift ( ) ; if ( futureAction ) { const p = ( actions [ actionName ] as any ) ( ... futureAction . args ) as Promise < any > ; if ( p == null ) { processNextConflictAction ( actionName , actions , conflictActions ) ; } else { p . then ( ( res ) => { if ( res === null ) { processNextConflictAction ( actionName , actions , conflictActions ) ; } futureAction . resolve ( res ) ; } ) . catch ( ( e ) => futureAction . reject ( e ) ) ; } } } } internal export function computeDerivedValues < S , A , P , DS > ( s , p , derivedStateRef , options ) { if ( options ?. derivedState == null ) { return false ; } let hasChanged = false ; const compare = globalConfig . comparator ; const depsMap = derivedStateRef . current . deps ; const ctx = buildInvocationContextBase ( s , null , p ) ; const keys = Object . keys ( options . derivedState ) as ( keyof DS ) [ ] ; derivedStateRef . current . state = keys . reduce ( ( acc , propName ) => { let compute = false ; const previousDeps = depsMap [ propName ] ; const deps = options . derivedState [ propName ] . getDeps ( ctx ) ; if ( previousDeps == null ) { compute = true ; } else { compute = deps . find ( ( dep , index ) => ( previousDeps . length < index ? true : ! compare ( previousDeps [ index ] , dep ) ) ) != null ; } hasChanged = hasChanged || compute ; depsMap [ propName ] = deps ; if ( compute ) { acc [ propName ] = options . derivedState [ propName ] . get ( ctx ) ; } else { acc [ propName ] = derivedStateRef . current . state [ propName ] ; } return acc ; } , { } as DS ) ; return hasChanged ; } internal export function onPropChange < S , P , A , DS > ( stateRef , derivedStateRef , propsRef , oldProps , actionsRef , options , setState , isInit ) { const hasDerivedState = options ?. derivedState != null ; if ( options . onPropsChange == null ) { if ( options ?. derivedState != null ) { setState ( undefined , undefined , 'onPropsChange' , 'effects' , false , null , true ) ; } return ; } let onPropsChanges ; if ( Array . isArray ( options . onPropsChange ) ) { onPropsChanges = options . onPropsChange ; } else { onPropsChanges = [ options . onPropsChange ] ; } let stateChanged = false ; const newStateRef = createRef ( stateRef . current ) ; const sideEffects = [ ] ; const compare = globalConfig . comparator ; onPropsChanges . forEach ( ( propsChange , index ) => { const getDeps = propsChange . getDeps ; if ( getDeps ) { let propChanged = false ; const indices = [ ] ; if ( isInit ) { if ( propsChange . onMount ) { propChanged = true ; } else { return ; } } else { const oldValues = getDeps ( oldProps ) ; const newValues = getDeps ( propsRef . current ) ; if ( oldValues . length !== newValues . length ) { console . warn ( 'options.onPropsChange.getDeps returned array must be stable (same length)' ) ; propChanged = true ; } else { oldValues . forEach ( ( v , i ) => { if ( ! compare ( v , newValues [ i ] ) ) { propChanged = true ; indices . push ( i ) ; } } ) ; } } if ( propChanged ) { const ctx = buildOnPropChangeEffects ( newStateRef , derivedStateRef , propsRef , indices , index , isInit ) ; const newState = propsChange . effects ?. ( ctx ) ?? null ; if ( newState != null ) { stateChanged = true ; newStateRef . current = newState ; setState ( newStateRef . current , undefined , 'onPropsChange' , 'effects' , false , ctx , true , isInit ) ; } if ( propsChange . sideEffects ) { sideEffects . push ( { index , indices , ctx } ) ; } } } } ) ; if ( ! stateChanged && hasDerivedState ) { setState ( undefined , undefined , 'onPropsChange' , 'effects' , false , null , true ) ; } if ( sideEffects . length > 0 ) { sideEffects . forEach ( ( sideEffectInfo ) => { const onPropChange = onPropsChanges [ sideEffectInfo . index ] ; onPropChange . sideEffects ( { ... addStateToContext ( addContextActions ( sideEffectInfo . ctx , actionsRef ) , newStateRef ) , indices : sideEffectInfo . indices , } ) ; } ) ; } } internal export function runMiddlewares < S , A extends DecoratedActions , P > ( middlewares , oldState , newState , loading , actionName , actionType , isAsync , actionCtx ) { if ( actionName === 'onPropsChange' && newState === undefined ) { return { newState : null , newLoading : false , } ; } if ( middlewares && middlewares . length > 0 ) { const res = middlewares . reduce ( ( acc , middleware ) => { const res = middleware . effects ( { isAsync , name : actionName , type : actionType , context : actionCtx , } , acc . oldState , acc . newState , loading ) ; return { newState : res ?. state || acc . newState , oldState : res ?. state ? acc . newState : acc . oldState , loading : res ?. loading == null ? acc . loading : res . loading , } ; } , { oldState , newState , loading , } ) ; return { newLoading : res . loading , newState : res . newState , } ; } return { newState , newLoading : loading , } ; }