import { SingleEffectorFactory } from "./effects"; import { IEffectorFactory, IFx, IStore, Params, StoreEvent, StoreEventFn, StorePropsEventFn, } from "./model"; /** * Create an Event handler that updates the Store state */ export function store( fn: StoreEventFn, ): () => StoreEvent; /** * Create an Event handler that updates the Store state * with whatever params you pass to it. */ export function store( fn: StorePropsEventFn, ): (...params: P) => StoreEvent; /** * Create an Event handler that updates the Store State */ export function store( fn: StoreEventFn | StorePropsEventFn, ): (...params: P) => StoreEvent { // NOTE: if we ever add interceptors, it might be useful // to have this delegate to fx(). For now, though, this // is somewhat more efficient if (fn.length === 1) { // no params, only state return () => fn as StoreEvent; } const withParams = fn as StorePropsEventFn; return (...params: P) => { return ( state: State, _: IStore, ) => withParams(state, ...params); }; } /** * Create an Effectful Event Handler. * * @return a factory that creates StoreEvent instances. The arguments you pass * to this factory will be passed along to your handler affector the first * argument, which you use to enqueue effects. */ export function fx( handler: (fx: IFx, ...params: P) => void, ): (...params: P) => StoreEvent { const effectors: IEffectorFactory = new SingleEffectorFactory(); const eventFactory = (...params: P) => { // the StoreEvent is a function return (old: State, theStore: IStore) => { const effector = effectors.acquire(old); // process the event handler(effector, ...params); // dispatch effects effector.invokeQueued(theStore); // return the new state (if any) and release the effector const newState = effector.state; effectors.release(effector); return newState; }; }; eventFactory.effectors = effectors; return eventFactory; }