M.computed() and M.effect() track their own
dependencies at runtime. See the Models showcase for the underlying model API.
A computed is lazy: its body does not run until something reads it, and the cached value is reused until a field it actually read changes. The counters below show how often each body runs.
Change Price or Quantity and the counter rises. Type in Note — the model updates, but the computed never re-runs. Press Read total again to confirm repeated reads are served from cache.
const order = M.create(
{ price: {}, qty: {}, note: {} },
{ price: 10, qty: 3, note: '' }
);
const total = M.computed(() => order.get('price') * order.get('qty'));
total.get(); // 30 — body runs now
total.get(); // 30 — cached, body does not run
order.set('note', 'hi'); // 'note' was never read → no re-evaluation
total.get(); // 30 — still cached
order.set('qty', 4);
total.get(); // 40 — a dependency moved, so it re-evaluates
An effect runs immediately to collect its dependencies, then again whenever any of them change. It replaces hand-wiring a subscription and comparing field names by hand.
Changing Session id does not re-run the effect — it is read inside
M.untracked().
const stop = M.effect(() => {
const user = prefs.get('username'); // tracked
const theme = prefs.get('theme'); // tracked
// Read without subscribing to it
const session = M.untracked(() => prefs.get('sessionId'));
render(user, theme, session);
});
stop(); // unsubscribe
Writes never recompute anything synchronously. They mark dependents dirty and schedule a single
microtask flush, so a burst of writes collapses into one pass. Use M.flush() when you
need a derived value to settle immediately.
Either button adds exactly one run: three writes in the same tick share one flush.
// Three separate writes, same tick → ONE effect run
form.set('a', 1);
form.set('b', 2);
form.set('c', 3);
// One batched write → also ONE effect run
form.set({ a: 1, b: 2, c: 3 });
// Need it settled right now (tests, or reading straight after a write)?
form.set('a', 9);
M.flush();
Dependencies are re-collected on every run, so a derivation stops listening to the branch it no longer takes. Switch the mode below and watch which input still triggers a re-run.
In Read X mode, editing Y does nothing. Switch modes and the roles swap.
M.effect(() => {
// Only ONE of x / y is a dependency at any moment
const value = state.get('mode') === 'x'
? state.get('x')
: state.get('y');
log(value);
});
Domma.component() uses tracking automatically — the definition syntax is unchanged.
Computeds re-evaluate only when a field they read changes, and a burst of writes produces one
render.
Touch unrelated field writes to the model but triggers neither a computed re-evaluation nor a render.
Domma.component('order-summary', {
template: '<p>{{label}}</p>{{#if freeDelivery}}<em>Free delivery</em>{{/if}}',
data() { return { items: 1, unitPrice: 25, discount: false, note: '' }; },
computed: {
// Re-evaluates only when items / unitPrice / discount change
label() {
const gross = this.data.items * this.data.unitPrice;
return `${this.data.items} item(s) — £${this.data.discount ? gross * 0.9 : gross}`;
},
freeDelivery() { return this.data.items * this.data.unitPrice > 50; }
}
});
await, so anything read after it is invisible to the graph. Fetch first, then write the
result to the model.
| Method | Returns | Description |
|---|---|---|
M.computed(fn, options?) |
ComputedRef |
Lazily-evaluated derived value. Caches until a tracked dependency changes. |
M.effect(fn, options?) |
Function |
Runs now, and again on dependency change. Returns a stop function. |
M.untracked(fn) |
any |
Read values without registering them as dependencies. |
M.flush() |
void |
Settle pending reactive work immediately rather than on the microtask. |
model.tracked() |
Proxy |
Read-tracked, write-through view. Writes still validate, notify and persist. |
ref.get() |
any |
Current value; registers a dependency on the caller. |
ref.peek() |
any |
Current value without registering a dependency. |
ref.dispose() |
void |
Unlink from the dependency graph. |
docs/Reactivity.md.