UNPKG

mutoid

Version:

Reactive library for data fetching, caching, state management

76 lines (75 loc) 2.94 kB
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' }), }); }; }