import { Observable, Subject, BehaviorSubject } from 'rxjs'; import { map, distinctUntilChanged, filter } from 'rxjs/operators'; import { Reducer } from './models/reducer'; import { ActionEvent } from './models/action-event'; export class Store extends BehaviorSubject { private reducers: Array>; private readonly _actions$ = new Subject>(); readonly actions$: Observable> = this._actions$.asObservable(); constructor(initialState: State, reducer: Reducer) constructor(initialState: State, reducers: Array>) constructor( initialState: State, reducerOrReducers: Reducer | Array> ) { super(initialState); this.reducers = typeof reducerOrReducers === 'function' ? [reducerOrReducers] : reducerOrReducers; } dispatch(action: ActionType, payload: Actions[ActionType]) { const oldState = this.value; const newState = this.reducers.reduce((updatedState, reducer) => { return reducer(updatedState, action, payload); }, oldState); this.next(newState); this._actions$.next({ action, payload, oldState, newState }); } select(key: K): Observable select(mapFunction: (value: State) => State[K]): Observable select(keyOrMapFn: (K) | ((value: State) => State[K])): Observable { const mapFn: any = typeof keyOrMapFn === 'string' ? value => value[keyOrMapFn] : keyOrMapFn; return this.asObservable() .pipe( map((mapFn) as () => any), distinctUntilChanged() ); } actionOfType(action: K): Observable> { return this._actions$.pipe(filter(actionEvent => actionEvent.action === action)); } mountChildState( propertyKey: string, initialState: any, reducerOrReducers: Reducer | Array, ) { this.reducers = this.reducers.concat(reducerOrReducers); this.next({ ...this.value, [propertyKey]: initialState }); } }