mutoid
Version:
Reactive library for data fetching, caching, state management
76 lines (75 loc) • 2.94 kB
JavaScript
import { BehaviorSubject, firstValueFrom, Observable } from 'rxjs';
import { take, takeUntil } from 'rxjs/operators';
// constructor
export const ctor = (c) => {
const subscribers = new Set();
let currentState = c.initState;
const getState = () => currentState;
const setState = (s) => {
currentState = s;
for (const subscriber of subscribers) {
subscriber.next(s);
}
};
const state$ = new Observable(subscriber => {
subscriber.next(currentState);
subscribers.add(subscriber);
return () => subscribers.delete(subscriber);
});
const subscribe = (listener) => {
const sub = state$.subscribe(() => {
listener();
});
return () => {
sub.unsubscribe();
};
};
return {
name: c.name,
subscribe,
getState,
setState,
state$,
notifier$: new BehaviorSubject({ type: 'initStore', name: c.name }),
initState: c.initState,
};
};
// mutation
export const ctorMutation = (name, effect) => ({ name, effect });
export const ctorMutationC = (name) => (effect) => ctorMutation(name, effect);
export const ctorMutationCR = (name) => (effectR) => r => ctorMutation(name, effectR(r));
// partialMutation
export const ctorPartialMutation = (name, filterPredicate, effect) => ({ name, filterPredicate, effect });
export const ctorPartialMutationC = (name, filterPredicate) => (effect) => ctorPartialMutation(name, filterPredicate, effect);
export const ctorPartialMutationCR = (name, filterPredicate) => (effectR) => (r) => ctorPartialMutation(name, filterPredicate, effectR(r));
// runner
export const toTask = (store) => () => firstValueFrom(store.state$.pipe(take(1)));
const buildRun = (pm, s, notifierTakeUntil) => {
if (notifierTakeUntil) {
return pm(s).pipe(takeUntil(notifierTakeUntil));
}
return pm(s);
};
export function mutationRunner(store, mutationR, options) {
return (...payload) => {
const mutation = mutationR(options?.deps);
const baseNotify = (state, date) => ({
name: store.name,
mutationName: mutation.name,
state,
date: date.toISOString(),
payload: payload,
});
store.notifier$.next({ ...baseNotify(store.getState(), new Date()), type: 'mutationLoad' });
const s = store.getState();
if (mutation.filterPredicate && mutation.filterPredicate(s) === false) {
return null;
}
store.notifier$.next({ ...baseNotify(store.getState(), new Date()), type: 'mutationStart' });
const pm = mutation.effect(...payload);
return buildRun(pm, s, options?.notifierTakeUntil).subscribe({
next: ns => store.setState(ns),
complete: () => store.notifier$.next({ ...baseNotify(store.getState(), new Date()), type: 'mutationEnd' }),
});
};
}