import deepEqual from 'fast-deep-equal/es6' ; import produce , { Draft } from 'immer' ; export type SelectorFn < T , U > = ( state : T ) => U ; interface Selector < T , U > { callback : ( newValue : U ) => void ; currentValue : U ; selectorFn : SelectorFn < T , U > ; } export class StateManager < T > { private internalData : Readonly < T > ; private publishTimer : any = null ; private subscribers = new Map < ( newValue : any ) => void , Selector < T , any > > ( ) ; constructor ( initialState ) { this . internalData = initialState ; } get state ( ) : Readonly < T > { return this . internalData ; } mutate ( mutation ) { this . internalData = produce ( this . internalData , mutation ) ; clearTimeout ( this . publishTimer ) ; this . publishTimer = setTimeout ( ( ) => { this . publish ( ) ; } , 0 ) ; } publish ( ) { for ( const [ callback , selector ] of this . subscribers ) { const { currentValue , selectorFn } = selector ; const newValue = selectorFn ( this . internalData ) ; if ( deepEqual ( newValue , currentValue ) ) { continue ; } this . subscribers . set ( callback , { ... selector , currentValue : newValue , } ) ; callback . call ( null , newValue ) ; } } subscribe < U > ( selectorFn , callback ) { this . subscribers . set ( callback , { callback , currentValue : selectorFn . call ( null , this . internalData ) , selectorFn , } ) ; return ( ) => this . unsubscribe ( callback ) ; } unsubscribe ( callback ) { this . subscribers . delete ( callback ) ; } DEBUG_destroy ( ) { clearTimeout ( this . publishTimer ) ; for ( const callback of this . subscribers . keys ( ) ) { this . subscribers . delete ( callback ) ; } } }