@tanstack/store
Version:
Framework agnostic type-safe store w/ reactive framework adapters
223 lines (222 loc) • 6.04 kB
JavaScript
import { createReactiveSystem, ReactiveFlags, getBatchDepth } from "./alien.js";
function toObserver(nextHandler, errorHandler, completionHandler) {
const isObserver = typeof nextHandler === "object";
const self = isObserver ? nextHandler : void 0;
return {
next: (isObserver ? nextHandler.next : nextHandler)?.bind(self),
error: (isObserver ? nextHandler.error : errorHandler)?.bind(self),
complete: (isObserver ? nextHandler.complete : completionHandler)?.bind(
self
)
};
}
const queuedEffects = [];
let cycle = 0;
const { link, unlink, propagate, checkDirty, shallowPropagate } = createReactiveSystem({
update(atom) {
return atom._update();
},
// eslint-disable-next-line no-shadow
notify(effect2) {
queuedEffects[queuedEffectsLength++] = effect2;
effect2.flags &= ~ReactiveFlags.Watching;
},
unwatched(atom) {
if (atom.depsTail !== void 0) {
atom.depsTail = void 0;
atom.flags = ReactiveFlags.Mutable | ReactiveFlags.Dirty;
purgeDeps(atom);
}
}
});
let notifyIndex = 0;
let queuedEffectsLength = 0;
let activeSub;
function purgeDeps(sub) {
const depsTail = sub.depsTail;
let dep = depsTail !== void 0 ? depsTail.nextDep : sub.deps;
while (dep !== void 0) {
dep = unlink(dep, sub);
}
}
function flush() {
if (getBatchDepth() > 0) {
return;
}
while (notifyIndex < queuedEffectsLength) {
const effect2 = queuedEffects[notifyIndex];
queuedEffects[notifyIndex++] = void 0;
effect2.notify();
}
notifyIndex = 0;
queuedEffectsLength = 0;
}
function createAsyncAtom(getValue, options) {
const ref = {};
const atom = createAtom(() => {
getValue().then(
(data) => {
const internalAtom = ref.current;
if (internalAtom._update({ status: "done", data })) {
const subs = internalAtom.subs;
if (subs !== void 0) {
propagate(subs);
shallowPropagate(subs);
flush();
}
}
},
(error) => {
const internalAtom = ref.current;
if (internalAtom._update({ status: "error", error })) {
const subs = internalAtom.subs;
if (subs !== void 0) {
propagate(subs);
shallowPropagate(subs);
flush();
}
}
}
);
return { status: "pending" };
}, options);
ref.current = atom;
return atom;
}
function createAtom(valueOrFn, options) {
const isComputed = typeof valueOrFn === "function";
const getter = valueOrFn;
const atom = {
_snapshot: isComputed ? void 0 : valueOrFn,
subs: void 0,
subsTail: void 0,
deps: void 0,
depsTail: void 0,
flags: isComputed ? ReactiveFlags.None : ReactiveFlags.Mutable,
get() {
if (activeSub !== void 0) {
link(atom, activeSub, cycle);
}
return atom._snapshot;
},
subscribe(observerOrFn) {
const obs = toObserver(observerOrFn);
const observed = { current: false };
const e = effect(() => {
atom.get();
if (!observed.current) {
observed.current = true;
} else {
obs.next?.(atom._snapshot);
}
});
return {
unsubscribe: () => {
e.stop();
}
};
},
_update(getValue) {
const prevSub = activeSub;
const compare = options?.compare ?? Object.is;
activeSub = atom;
++cycle;
atom.depsTail = void 0;
if (isComputed) {
atom.flags = ReactiveFlags.Mutable | ReactiveFlags.RecursedCheck;
}
try {
const oldValue = atom._snapshot;
const newValue = typeof getValue === "function" ? getValue(oldValue) : getValue === void 0 && isComputed ? getter(oldValue) : getValue;
if (oldValue === void 0 || !compare(oldValue, newValue)) {
atom._snapshot = newValue;
return true;
}
return false;
} finally {
activeSub = prevSub;
if (isComputed) {
atom.flags &= ~ReactiveFlags.RecursedCheck;
}
purgeDeps(atom);
}
}
};
if (isComputed) {
atom.flags = ReactiveFlags.Mutable | ReactiveFlags.Dirty;
atom.get = function() {
const flags = atom.flags;
if (flags & ReactiveFlags.Dirty || flags & ReactiveFlags.Pending && checkDirty(atom.deps, atom)) {
if (atom._update()) {
const subs = atom.subs;
if (subs !== void 0) {
shallowPropagate(subs);
}
}
} else if (flags & ReactiveFlags.Pending) {
atom.flags = flags & ~ReactiveFlags.Pending;
}
if (activeSub !== void 0) {
link(atom, activeSub, cycle);
}
return atom._snapshot;
};
} else {
atom.set = function(valueOrFn2) {
if (atom._update(valueOrFn2)) {
const subs = atom.subs;
if (subs !== void 0) {
propagate(subs);
shallowPropagate(subs);
flush();
}
}
};
}
return atom;
}
function effect(fn) {
const run = () => {
const prevSub = activeSub;
activeSub = effectObj;
++cycle;
effectObj.depsTail = void 0;
effectObj.flags = ReactiveFlags.Watching | ReactiveFlags.RecursedCheck;
try {
return fn();
} finally {
activeSub = prevSub;
effectObj.flags &= ~ReactiveFlags.RecursedCheck;
purgeDeps(effectObj);
}
};
const effectObj = {
deps: void 0,
depsTail: void 0,
subs: void 0,
subsTail: void 0,
flags: ReactiveFlags.Watching | ReactiveFlags.RecursedCheck,
notify() {
const flags = this.flags;
if (flags & ReactiveFlags.Dirty || flags & ReactiveFlags.Pending && checkDirty(this.deps, this)) {
run();
} else {
this.flags = ReactiveFlags.Watching;
}
},
stop() {
this.flags = ReactiveFlags.None;
this.depsTail = void 0;
purgeDeps(this);
}
};
run();
return effectObj;
}
export {
createAsyncAtom,
createAtom,
flush,
toObserver
};
//# sourceMappingURL=atom.js.map