UNPKG

@zedux/machines

Version:

Simple native state machine implementation for Zedux atoms

151 lines (150 loc) 6.96 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.injectMachineStore = void 0; const atoms_1 = require("@zedux/atoms"); const MachineStore_1 = require("./MachineStore"); /** * Create a MachineStore. Pass a statesFactory * * The first state in the state list returned from your statesFactory will * become the initial state (`.value`) of the store. * * Registers an effect that listens to all store changes and calls the * configured listeners appropriately. * * ```ts * const store = injectMachineStore(state => [ * state('a') * .on('next', 'b', localGuard) * .onEnter(enterListener) * .onLeave(leaveListener), * state('b').on('next', 'a') * ], initialContext, { guard, onTransition }) * ``` * * Set a universal transition guard via the 3rd `config` object param. This * guard will be called every time a valid transition is about to occur. It will * be called with the current `.context` value and should return a boolean. * Return true to allow the transition, or any falsy value to deny it. * * Set a universal `onTransition` listener via the 3rd `config` object param. * This listener will be called every time the machine transitions to a new * state (after the state is updated). It will be called with 2 params: The * current MachineStore and the storeEffect of the action that transitioned the * store. For example, use `storeEffect.oldState.value` to see what state the * machine just transitioned from. * * @param statesFactory Required. A function. Use the received state factory to * create a list of states for the machine and specify their transitions, * guards, and listeners. * @param initialContext Optional. An object or undefined. Will be set as the * initial `.context` value of the machine store's state. * @param config Optional. An object with 2 additional properties: `guard` and * `onTransition`. */ const injectMachineStore = (...[statesFactory, initialContext, config]) => { const instance = (0, atoms_1.injectSelf)(); const { enterHooks, leaveHooks, store } = (0, atoms_1.injectMemo)(() => { var _a, _b, _c; const enterHooks = {}; const leaveHooks = {}; const states = {}; const createState = (stateName) => { const state = { on: (eventName, nextState, guard) => { if (!states[stateName]) { states[stateName] = {}; } if (!states[nextState]) { states[nextState] = {}; } states[stateName][eventName] = { name: nextState, guard, }; return state; }, onEnter: (callback) => { if (!enterHooks[stateName]) { enterHooks[stateName] = []; } enterHooks[stateName].push(callback); return state; }, onLeave: (callback) => { if (!leaveHooks[stateName]) { leaveHooks[stateName] = []; } leaveHooks[stateName].push(callback); return state; }, stateName, }; return state; }; const [initialState] = statesFactory(createState); const hydration = (config === null || config === void 0 ? void 0 : config.hydrate) && ((_a = instance.ecosystem.hydration) === null || _a === void 0 ? void 0 : _a[instance.id]); const store = new MachineStore_1.MachineStore((_b = hydration === null || hydration === void 0 ? void 0 : hydration.value) !== null && _b !== void 0 ? _b : initialState.stateName, states, (_c = hydration === null || hydration === void 0 ? void 0 : hydration.context) !== null && _c !== void 0 ? _c : initialContext, config === null || config === void 0 ? void 0 : config.guard); return { enterHooks, leaveHooks, store }; }, []); const subscribeRef = (0, atoms_1.injectRef)(); subscribeRef.current = config === null || config === void 0 ? void 0 : config.subscribe; (0, atoms_1.injectEffect)(() => { const subscription = store.subscribe({ effects: storeEffect => { const { action, newState, oldState } = storeEffect; if (newState.value === (oldState === null || oldState === void 0 ? void 0 : oldState.value)) return; if (oldState && leaveHooks[oldState.value]) { leaveHooks[oldState.value].forEach(callback => callback(store, storeEffect)); } if (enterHooks[newState.value]) { enterHooks[newState.value].forEach(callback => callback(store, storeEffect)); } if (config === null || config === void 0 ? void 0 : config.onTransition) { config.onTransition(store, storeEffect); } // Nothing to do if the state hasn't changed. Also ignore state updates // during evaluation or that are caused by `zeduxTypes.ignore` actions if (!subscribeRef.current || newState === oldState || instance._isEvaluating || (action === null || action === void 0 ? void 0 : action.meta) === atoms_1.zeduxTypes.ignore) { return; } instance._scheduleEvaluation({ newState, oldState, operation: 'injectMachineStore', reasons: [ { action, newState, oldState, operation: 'dispatch', sourceType: 'Store', type: 'state changed', }, ], sourceType: 'Injector', type: 'state changed', }, false); // run the scheduler synchronously after any store update if ((action === null || action === void 0 ? void 0 : action.meta) !== atoms_1.zeduxTypes.batch) { instance.ecosystem._scheduler.flush(); } }, }); return () => subscription.unsubscribe(); }, [], { synchronous: true }); const currentState = store.getState(); if (enterHooks[currentState.value]) { enterHooks[currentState.value].forEach(callback => callback(store, { action: { type: atoms_1.zeduxTypes.prime }, newState: currentState, store, })); } return store; }; exports.injectMachineStore = injectMachineStore;