@sigi/core
Version:
Sigi core library
104 lines • 3.44 kB
JavaScript
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 `);
}
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