@platform/state
Version:
A small, simple, strongly typed, [rx/observable] state-machine.
76 lines (75 loc) • 2.38 kB
JavaScript
import produce from 'immer';
import { Subject } from 'rxjs';
import { filter, map, share, takeUntil } from 'rxjs/operators';
export class Store {
constructor(args) {
this._dispose$ = new Subject();
this._changing$ = new Subject();
this._changed$ = new Subject();
this._ = {
state: undefined,
};
this.dispose$ = this._dispose$.pipe(share());
this.changing$ = this._changing$.pipe(share());
this.changed$ = this._changed$.pipe(share());
this._.state = Object.assign({}, args.initial);
this._event$ = args.event$ || new Subject();
this.event$ = this._event$.pipe(takeUntil(this.dispose$), map((e) => this.toDispatchEvent(e)), share());
}
static create(args) {
return new Store(args);
}
dispose() {
this._dispose$.next();
this._dispose$.complete();
}
get isDisposed() {
return this._dispose$.isStopped;
}
get state() {
return Object.assign({}, this._.state);
}
dispatch(event) {
this._event$.next(event);
return this;
}
on(type) {
return this.event$.pipe(filter((e) => e.type === type), map((e) => e));
}
toDispatchEvent(event) {
const { type, payload } = event;
const from = this.state;
const result = {
type,
payload,
get state() {
return Object.assign({}, from);
},
change: (next) => {
const to = typeof next !== 'function'
? Object.assign({}, next) : produce(from, (draft) => {
next(draft);
return undefined;
});
let isCancelled = false;
const change = { type, event, from, to };
this._changing$.next({
change,
isCancelled,
cancel: () => (isCancelled = true),
});
if (isCancelled) {
return result;
}
this._.state = to;
this._changed$.next(change);
return result;
},
dispatch: (event) => {
this.dispatch(event);
return result;
},
};
return result;
}
}