UNPKG

@zedux/atoms

Version:

A Molecular State Engine for React

93 lines (92 loc) 3.98 kB
import { Store, is } from '@zedux/core'; import { Static } from '../utils/index.js'; import { pluginActions } from '../utils/plugin-actions.js'; import { AtomInstanceBase } from './instances/AtomInstanceBase.js'; /** * A stack of AtomInstances and AtomSelectors that are currently evaluating - * innermost instance/selector (the one that's actually currently evaluating) at * the end of the array. * * This has to live in the module scope so `readInstance` can access it without * any ecosystem context. That's how injectors work. */ export let stack = []; export const readInstance = () => { const item = stack[stack.length - 1]; if (true /* DEV */ && !is(item === null || item === void 0 ? void 0 : item.node, AtomInstanceBase)) { throw new Error('Zedux: Injectors can only be used in atom state factories'); } return item.node; }; export const setStack = (newStack) => (stack = newStack); export class EvaluationStack { constructor(ecosystem) { this.ecosystem = ecosystem; const { _graph, selectors } = ecosystem; const get = ((atom, params) => { const { id, store } = ecosystem.getInstance(atom, params); // when called outside evaluation, get() is just an alias for // ecosystem.get() if (!stack.length) return store.getState(); // if get is called during evaluation, track the required atom instances so // we can add graph edges for them _graph.addEdge(stack[stack.length - 1].node.id, id, 'get', 0); return store.getState(); }); const getInstance = (atom, params, edgeInfo) => { var _a; const instance = ecosystem.getInstance(atom, params); // when called outside evaluation, getInstance() is just an alias for // ecosystem.getInstance() if (!stack.length) return instance; // if getInstance is called during evaluation, track the required atom // instances so we can add graph edges for them _graph.addEdge(stack[stack.length - 1].node.id, instance.id, (edgeInfo === null || edgeInfo === void 0 ? void 0 : edgeInfo[1]) || 'getInstance', (_a = edgeInfo === null || edgeInfo === void 0 ? void 0 : edgeInfo[0]) !== null && _a !== void 0 ? _a : Static); return instance; }; const select = (selectable, ...args) => { // when called outside evaluation, select() is just an alias for // ecosystem.select() if (!stack.length) { return ecosystem.select(selectable, ...args); } const cache = selectors.getCache(selectable, args); _graph.addEdge(stack[stack.length - 1].node.id, cache.id, 'select', 0); return cache.result; }; this.atomGetters = { ecosystem, get, getInstance, select, }; } finish() { const item = stack.pop(); const { _idGenerator, _mods, modBus } = this.ecosystem; // if we just popped the last thing off the stack, restore the default // scheduler if (!stack.length) Store._scheduler = undefined; if (!item || !_mods.evaluationFinished) return; const time = item.start ? _idGenerator.now(true) - item.start : 0; const action = { node: item.node, time }; modBus.dispatch(pluginActions.evaluationFinished(action)); } read() { return stack[stack.length - 1]; } start(node) { const { _idGenerator, _mods, _scheduler } = this.ecosystem; const newItem = { node, start: _mods.evaluationFinished ? _idGenerator.now(true) : undefined, }; stack.push(newItem); // all stores created during evaluation automatically belong to the ecosystem Store._scheduler = _scheduler; } }