Derived Atoms
What derive() does
derive(key, compute) creates a normal Atom<T> whose value is produced by a pure compute function. The function receives a get helper; any atom read through get becomes a tracked dependency. When any dependency changes, the compute function re-runs and the derived atom’s value updates automatically.
The returned atom is a full Atom<T> — you can subscribe to it with onChange, read .value, use it in React with useValue(derived), even chain it into another derive.
Basic example
import { createAtom, derive } from "@mongez/atom";
const firstName = createAtom({ key: "first", default: "Ada" });const lastName = createAtom({ key: "last", default: "Lovelace" });
const fullName = derive("fullName", get => `${get(firstName)} ${get(lastName)}`);
fullName.value; // "Ada Lovelace"firstName.update("Grace");fullName.value; // "Grace Lovelace"Multiple sources
const priceAtom = createAtom({ key: "price", default: 100 });const quantityAtom = createAtom({ key: "quantity", default: 3 });const taxRateAtom = createAtom({ key: "taxRate", default: 0.1 });
const totalAtom = derive("total", get => { const subtotal = get(priceAtom) * get(quantityAtom); return subtotal * (1 + get(taxRateAtom));});
totalAtom.value; // 330quantityAtom.update(5);totalAtom.value; // 550Conditional reads — dynamic dependency graph
The dependency set is re-evaluated on every run. Branches that are not taken on a given run do not create a subscription for that run:
const branch = createAtom({ key: "branch", default: "first" as "first" | "last",});
const selected = derive("selected", get => get(branch) === "first" ? get(firstName) : get(lastName));
selected.value; // "Grace" (reads firstName)branch.update("last");selected.value; // "Lovelace" (now reads lastName, dropped firstName dep)When branch flips to "last", selected unsubscribes from firstName and subscribes to lastName. Memory-safe: no stale subscriptions accumulate.
Chaining derivations
A derived atom that reads another derived atom recomputes when either level changes:
const cartAtom = atomCollection<{ price: number; qty: number }>({ key: "cart3" });const promoAtom = createAtom({ key: "promo", default: 0 });
const subtotal = derive("subtotal", get => get(cartAtom).reduce((s, i) => s + i.price * i.qty, 0));
const finalPrice = derive("finalPrice", get => get(subtotal) * (1 - get(promoAtom)));
cartAtom.push({ price: 50, qty: 2 });finalPrice.value; // 100promoAtom.update(0.2);finalPrice.value; // 80Subscribing to a derived atom
Derived atoms are normal Atom<T> instances; all subscription APIs work:
const sub = fullName.onChange((next, prev) => { console.log("Name changed:", next);});sub.unsubscribe();
// In @mongez/react-atom:// const name = useValue(fullName);Cleanup / destroying a derived atom
When you destroy() a derived atom, it automatically unsubscribes from all tracked dependencies. No manual cleanup is needed:
fullName.destroy();// All dependency subscriptions (firstName, lastName) are released.Signature reference
function derive<T>( key: string, compute: (get: DeriveGetter) => T, options?: { register?: boolean }, // false = skip global registry): Atom<T>;
// DeriveGetter:type DeriveGetter = <V>(atom: Atom<V, any>) => V;Key pitfalls
- Never write to a derived atom directly.
derivedAtom.update(x)works at runtime but the next dependency change will overwrite it. Use a regularcreateAtomif you need writable state. - Object return values always re-trigger because the compute function builds a new object reference every call. For primitives, the atom’s
update()short-circuits on===equality — soderive("flag", get => get(a) > 0)will only notify subscribers when the boolean value actually changes. - Errors in compute are surfaced asynchronously via
queueMicrotask. The atom keeps its previous value on error. This prevents one broken derivation from crashing the source atom’s update cycle — but errors may appear in the console later than expected. - Keys must be unique just like any atom key. Use a namespaced key:
"cart.totalPrice"not just"total". - Initial computation is eager.
derive(...)runs the compute function once immediately on creation. If a dependency atom doesn’t exist yet at that moment, it will beundefined. Register all source atoms before their derived counterparts.