import { BehaviorSubject } from 'rxjs'; import { IStore } from './IStore'; import { proxyToBehaviorSubject } from './proxyToBehaviorSubject'; import { onFirstSubscription } from './onFirstSubscription'; export type IStateUpdaterFunc = ( state: State ) => Partial; export type IStateUpdater = | Partial | IStateUpdaterFunc; /** * Prostore which uses BehaviorSubject to hold its state. * It mimics the React class component state management. * You can use setState to change part of the state of the store. * * It performs synchronous state updates which means that two consequent calls to setState * will result in dispatching two store updates. */ export class BehaviorStore implements IStore { state$: BehaviorSubject; get state() { return this.state$.value; } constructor(initialState: State) { const subject$ = new BehaviorSubject(initialState); this.state$ = proxyToBehaviorSubject( subject$.pipe( onFirstSubscription( () => this.onSubscribe(), () => this.onUnsubscribe() ) ), subject$ ); } /** * This callback is called when the first subscriber subscribes to this store. * It may be called multiple times during the lifetime of the store. * You can use it to perform side-effects like subscribing to other * observables. */ onSubscribe() {} /** * This callback is called when the last subscriber unsubscribes from this store. * It may be called multiple times during the lifetime of the store. * You can use it to cleanup side-effects created during onSubscribe. */ onUnsubscribe() {} /** * Update part of the store state. * * @param stateUpdate - object containing partial state update or updater function. * If the function is provided, it is called with the current state and should return the partial state update */ setState(stateUpdate: IStateUpdater) { this.state$.next(this._applyStateUpdater(this.state, stateUpdate)); } _applyStateUpdater(state: State, stateUpdate: IStateUpdater): State { if (typeof stateUpdate === 'function') { return { ...state, ...stateUpdate(state) }; } else { return { ...state, ...stateUpdate }; } } }