@zedux/atoms
Version:
A Molecular State Engine for React
93 lines (92 loc) • 3.98 kB
JavaScript
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;
}
}