@dhmk/atom
Version:
Lightweight mobx-like observable values, computed values and side-effects
130 lines (129 loc) • 4.73 kB
JavaScript
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
import { removeAtom, useAtom } from "../shared";
import { Id, AtomState, defaultAtomOptions, } from "../types";
import { runtime } from "../runtime";
var ComputedAtom = /** @class */ (function () {
function ComputedAtom(fn, options) {
this.fn = fn;
this.observers = new Map();
this.deps = Array();
this.prevDeps = Array();
this.prevDepsIndex = 0;
this.runId = new Id();
this.versionId = new Id();
this.isObserved = false;
this.depsForUnobserved = new Set();
this.isError = false;
this.state = AtomState.Stale;
this.depsVersions = Array();
this.options = __assign(__assign({}, defaultAtomOptions), options);
}
ComputedAtom.prototype.invalidate = function (state, isValueAtom) {
var oldState = this.state;
if (state > this.state)
this.state = state;
if (oldState === AtomState.Actual)
this.observers.forEach(function (_, a) {
return a.invalidate(AtomState.PossiblyStale, false);
});
};
ComputedAtom.prototype.calculate = function () {
this.prevDeps = this.deps;
this.deps = [];
this.prevDepsIndex = 0;
this.runId = new Id();
if (!this.isObserved && runtime.currentAtom) {
this.isObserved = true;
var onBO = this.options.onBecomeObserved;
if (onBO)
runtime.addEffect({ actualize: onBO });
}
if (!this.isObserved)
this.depsForUnobserved = new Set();
var nextValue;
var isError;
var prevAtom = runtime.currentAtom;
runtime.currentAtom = this;
try {
nextValue = this.fn();
}
catch (e) {
isError = true;
nextValue = e;
}
runtime.currentAtom = prevAtom;
if (!this.isObserved)
this.deps = Array.from(this.depsForUnobserved);
this.depsVersions.length = this.deps.length;
for (var i = 0; i < this.deps.length; ++i) {
var dep = this.deps[i];
dep.runId = this.runId;
this.depsVersions[i] = dep.versionId;
}
for (var i = this.prevDepsIndex; i < this.prevDeps.length; ++i) {
var a = this.prevDeps[i];
if (a.runId !== this.runId)
removeAtom(a, this);
}
this.prevDeps = [];
this.state = this.isObserved ? AtomState.Actual : AtomState.PossiblyStale;
if (!this.options.equals(nextValue, this.value)) {
this.value = nextValue;
this.isError = isError;
this.versionId = new Id();
this.observers.forEach(function (_, a) { return a.invalidate(AtomState.Stale, false); });
}
};
ComputedAtom.prototype.actualize = function () {
if (this.state === AtomState.Actual)
return;
if (this.state === AtomState.PossiblyStale) {
for (var i = 0; i < this.deps.length; ++i) {
var dep = this.deps[i];
dep.actualize();
if (dep.versionId !== this.depsVersions[i]) {
this.state = AtomState.Stale;
break;
}
}
}
if (this.state === AtomState.PossiblyStale)
this.state = AtomState.Actual;
if (this.state === AtomState.Stale)
this.calculate();
};
ComputedAtom.prototype.get = function () {
this.actualize();
useAtom(this);
if (this.isError)
throw this.value;
return this.value;
};
ComputedAtom.prototype.toJSON = function () {
return this.get();
};
ComputedAtom.prototype.dispose = function () {
if (this.isObserved) {
this.isObserved = false;
var onBUO = this.options.onBecomeUnobserved;
if (onBUO)
runtime.addEffect({ actualize: onBUO });
for (var i = 0; i < this.deps.length; ++i) {
removeAtom(this.deps[i], this);
}
this.state = AtomState.Stale;
}
};
return ComputedAtom;
}());
export { ComputedAtom };