UNPKG

@zedux/stores

Version:

The legacy composable store model of Zedux

106 lines (105 loc) 4.54 kB
import { createStore, zeduxTypes } from '@zedux/core'; import { zi } from '@zedux/atoms'; import { prefix } from './atoms-port.js'; export const doSubscribe = (instance, store) => store.subscribe((newState, p, action) => { // Nothing to do if the state hasn't changed. Also, ignore state updates // during evaluation. TODO: Create an ecosystem-level flag to turn on // warning logging for state-updates-during-evaluation, since this may be // considered an anti-pattern. if (instance._isEvaluating || action.meta === zeduxTypes.ignore) { return; } const isBatch = (action === null || action === void 0 ? void 0 : action.meta) === zeduxTypes.batch; instance.r({ p }, isBatch); // run the scheduler synchronously after every store update unless batching if (!isBatch) { instance.e._scheduler.flush(); } }); /** * injectStore() * * A convenience utility for quickly creating and optionally subscribing to * stores in atoms. * * The returned store is a stable reference - it will not change on subsequent * evaluations. It can therefore be returned from the instance factory as the * instance's store. It also doesn't _need_ to be added to injector deps arrays * (though there's no harm in doing so). * * Accepts either the initial store state or a function that returns the store. * Use the latter for maximum flexibility. * * Subscribes to the store by default, causing the atom to be reevaluated on * every state change. This can be changed by passing `subscribe: false`. * * In most cases you won't need to prevent subscribing. But it can be a useful * performance optimization. * * ```ts * import { atom, injectStore } from '@zedux/atoms' * * const inputAtom = atom('input', () => { * const store = injectStore('', { subscribe: false }) * * return store * }) * ``` * * When `hydrate: true` is passed, the store's initial state will be set to the * value from the last call to `ecosystem.hydrate()` whose key matches this atom * instance. The hydrated value will be passed to the atom's `hydrate` config * option, if any, to transform the value first. * * When the function `storeFactory` overload is used and `hydrate: true` is * passed, the transformed hydration will be passed to the store factory * function and it's up to you to use it to hydrate the store you create. * * ```ts * const store = injectStore( * hydration => createStore(null, hydration ?? defaultVal), * { hydrate: true } * ) * // or simply: * const store = injectStore(defaultVal, { hydrate: true }) * ``` * * @param storeFactory - Either a function that returns a store or the initial * state of the store * @param config - A config object. Accepts the following properties: * - `hydrate` - Whether to try hydrating this store with * - `subscribe` - Whether to subscribe to the store (default: `true`) * @returns Store */ export const injectStore = zi.c('injectStore', (instance, storeFactory, config) => { var _a, _b; const subscribe = (_a = config === null || config === void 0 ? void 0 : config.subscribe) !== null && _a !== void 0 ? _a : true; const getStore = typeof storeFactory === 'function' ? storeFactory : (hydration) => createStore(null, hydration !== null && hydration !== void 0 ? hydration : storeFactory); const store = getStore((config === null || config === void 0 ? void 0 : config.hydrate) ? (_b = instance.e.hydration) === null || _b === void 0 ? void 0 : _b[instance.id] : undefined); const subscription = subscribe && doSubscribe(instance, store); return { cleanup: subscription ? () => subscription.unsubscribe() : undefined, result: store, type: `${prefix}/store`, }; }, (prevDescriptor, instance, storeFactory, config) => { var _a; const subscribe = (_a = config === null || config === void 0 ? void 0 : config.subscribe) !== null && _a !== void 0 ? _a : true; const prevsubscribe = !!prevDescriptor.cleanup; if (prevsubscribe === subscribe) return prevDescriptor; // we were subscribed, now we're not if (!subscribe) { // cleanup must be defined here. This cast is fine: ; prevDescriptor.cleanup(); prevDescriptor.cleanup = undefined; return prevDescriptor; } // we weren't subscribed, now we are const subscription = doSubscribe(instance, prevDescriptor.result); prevDescriptor.cleanup = () => subscription.unsubscribe(); return prevDescriptor; });