UNPKG

@sigi/core

Version:
104 lines 3.44 kB
import { BehaviorSubject, ReplaySubject, Subject, Subscription, identity, NEVER } from 'rxjs'; import { last, share, switchMap, takeUntil } from 'rxjs/operators'; import { logStoreAction } from './logger'; import { INIT_ACTION_TYPE_SYMBOL, TERMINATE_ACTION_TYPE_SYMBOL, NOOP_ACTION_TYPE_SYMBOL } from './symbols'; export class Store { get state() { return this.internalState; } get ready() { return this.isReady; } constructor(name, reducer = identity, epic = () => NEVER) { this.state$ = new ReplaySubject(1); this.action$ = new Subject(); this.isReady = false; this.actionSub = new Subscription(); this.initAction = { type: INIT_ACTION_TYPE_SYMBOL, payload: null, store: this, }; this.name = name; this.reducer = reducer; this.epic$ = new BehaviorSubject(epic); } setup(defaultState) { this.internalState = defaultState; this.state$.next(defaultState); this.subscribeAction(); this.log(this.initAction); this.isReady = true; } addEpic(combineEpic) { const { epic$ } = this; const prevEpic = epic$.getValue(); epic$.next(combineEpic((action$) => { let output$; if (action$ instanceof Subject) { output$ = prevEpic(action$); } else { output$ = prevEpic(action$.pipe(share())); } return output$.pipe(takeUntil(this.action$.pipe(last(null, null)))); })); return () => { this.epic$.next(prevEpic); }; } dispatch(action) { if (action.type === NOOP_ACTION_TYPE_SYMBOL) { return; } if (action.store !== this) { action.store.dispatch(action); return; } const prevState = this.internalState; const newState = this.reducer(prevState, action); if (newState !== prevState) { if (process.env.NODE_ENV !== 'production' && newState === undefined) { console.warn(`${action.type} produced an undefined state, you may forget to return new State in @Reducer`); } this.internalState = newState; this.state$.next(newState); } this.log(action); this.action$.next(action); } log(action) { if (action.type !== TERMINATE_ACTION_TYPE_SYMBOL) { logStoreAction(action); } } dispose() { this.actionSub.unsubscribe(); this.action$.complete(); this.state$.complete(); this.epic$.complete(); } subscribeAction() { this.actionSub = this.epic$ .pipe(switchMap((epic) => epic(this.action$).pipe(takeUntil(this.action$.pipe(last(null, null)))))) .subscribe({ next: (action) => { try { this.dispatch(action); } catch (e) { if (process.env.NODE_ENV === 'development') { console.error(e); } this.action$.error(e); } }, error: (e) => { if (!this.action$.closed) { this.action$.error(e); } }, }); } } //# sourceMappingURL=store.js.map