overmind
Version:
Frictionless state management
84 lines • 3.44 kB
JavaScript
import { IS_PROXY, TrackStateTree, } from 'proxy-state-tree';
import { EventType } from './internalTypes';
export const IS_DERIVED = Symbol('IS_DERIVED');
export const IS_DERIVED_CONSTRUCTOR = Symbol('IS_DERIVED_CONSTRUCTOR');
export class Derived {
constructor(cb) {
this.cb = cb;
this.isDirty = true;
this.updateCount = 0;
const boundEvaluate = this.evaluate.bind(this);
if (process.env.NODE_ENV === 'development') {
boundEvaluate.dispose = () => {
this.disposeOnMutation();
};
}
boundEvaluate[IS_DERIVED] = true;
return boundEvaluate;
}
runScope(tree, path) {
const parent = path
.slice(0, path.length - 1)
.reduce((curr, key) => curr[key], tree.state);
return this.cb(parent, tree.state);
}
evaluate(eventHub, tree, proxyStateTree, path) {
if (!this.disposeOnMutation) {
this.disposeOnMutation = proxyStateTree.onMutation((_, paths, flushId) => {
if (typeof path.reduce((aggr, key) => aggr && aggr[key], proxyStateTree.sourceState) !== 'function') {
this.disposeOnMutation();
return;
}
if (this.isDirty) {
return;
}
for (const mutationPath of paths) {
if (this.paths.has(mutationPath)) {
this.isDirty = true;
eventHub.emitAsync(EventType.DERIVED_DIRTY, {
derivedPath: path,
path: mutationPath,
flushId,
});
return;
}
}
});
}
// During development we need to move the ownership of whatever state is returned from
// the derived to track it correctly. In production we only have one proxifier, so no worries
if (this.isDirty || this.previousProxifier !== tree.proxifier) {
const getPaths = tree.trackPaths();
this.value = this.runScope(tree, path);
this.isDirty = false;
this.paths = getPaths();
if (process.env.NODE_ENV === 'development') {
eventHub.emitAsync(EventType.DERIVED, {
path,
paths: Array.from(this.paths),
updateCount: this.updateCount,
value: this.value,
});
this.updateCount++;
}
}
if (tree instanceof TrackStateTree) {
// If we access a cached value we have to make sure that we move
// the tracked paths into the tree looking at it, where
// addTrackingPath is for initial tree and "trackPathListeners"
// is for nested derived
for (const path of this.paths) {
tree.addTrackingPath(path);
tree.trackPathListeners.forEach((cb) => cb(path));
}
}
this.previousProxifier = tree.proxifier;
// This value might be a proxy, we need to rescope
// it to the current tree looking
if (this.value && this.value[IS_PROXY]) {
return proxyStateTree.rescope(this.value, tree);
}
return this.value;
}
}
//# sourceMappingURL=derived.js.map