Skip to content

Stores

Problem

You have several related signals and computed values for a single feature. Keeping them as loose module-level variables makes the boundaries unclear — grouping them into a store object organizes ownership.

Solution

Basic Store

ts
import { store, watch, batch, computed } from '@vielzeug/stateit';

const cart = store({ items: [] as string[], total: 0 });

// Partial patch
cart.patch({ total: 42 });

// Updater function
cart.update((s) => ({ ...s, items: [...s.items, 'apple'] }));

// Watch a derived slice via computed()
const totalSignal = computed(() => cart.value.total);
watch(totalSignal, (total) => console.log('total:', total));

// Batch
batch(() => {
  cart.patch({ total: 0 });
  cart.update((s) => ({ ...s, items: [] }));
});

cart.reset();

Slice Watch via Getter + watch()

ts
import { store, watch } from '@vielzeug/stateit';

const user = store({ id: 1, name: 'Alice', role: 'admin' });

const sub = watch(() => user.value.name, (name, prev) => {
  console.log('name:', prev, '→', name);
});

user.patch({ role: 'editor' }); // ← does NOT fire (name unchanged)
user.patch({ name: 'Bob' }); // → "name: Alice → Bob"

sub.dispose();

Resetting to Initial State

reset() restores the state passed to store() and protects it from external mutation:

ts
import { store } from '@vielzeug/stateit';

const s = store({ count: 0, label: 'default' });
s.patch({ count: 10, label: 'modified' });
console.log(s.value); // { count: 10, label: 'modified' }

s.reset();
console.log(s.value); // { count: 0, label: 'default' }

Pitfalls

  • Computed values are recalculated lazily when accessed, not eagerly when dependencies change. Reading a computed in a setTimeout may return a stale value if it has not been accessed since the last signal update.
  • Exporting a writable store directly allows external code to mutate it and bypass your invariants. Export readonly(store) and explicit mutation functions instead.
  • Creating a store inside a factory function called multiple times creates independent instances. This is correct for per-component stores but wrong for shared module stores — create module-level stores outside any function.