Skip to content

3 · Store

A store is a thin convention layer over signals: named actions, a predictable shape, and reset() for free. Use stores when state has multiple consumers or when actions are non-trivial.

What to look at: the render fn reads cart.state.value, never the raw signal. Mutations go through cart.actions.* — there’s no set() exposed to the outside world.

src/main.tsx
import { defineStore, computed, mount, delegate } from 'kerfjs';
const cart = defineStore({
initial: () => ({
items: [
{ id: 'a', name: 'Saw', price: 32 },
{ id: 'b', name: 'Plane', price: 78 },
{ id: 'c', name: 'Chisel', price: 18 },
],
}),
actions: (set, get) => ({
remove: (id: string) => set({ items: get().items.filter((it) => it.id !== id) }),
clear: () => set({ items: [] }),
}),
});
const subtotal = computed(() =>
cart.state.value.items.reduce((s, it) => s + it.price, 0),
);
const fmt = (n: number) => `$${n}`;
const root = document.getElementById('app')!;
mount(root, () => (
<div class="kerf-stack" style="max-width: 24rem;">
<ul style="list-style: none; padding: 0; display: flex; flex-direction: column; gap: 0.4rem;">
{cart.state.value.items.map((it) => (
<li
data-key={it.id}
style="display: flex; align-items: center; gap: 0.75rem;"
>
<span style="flex: 1;">{it.name}</span>
<span class="kerf-mono">{fmt(it.price)}</span>
<button data-action="remove" data-id={it.id} aria-label={`Remove ${it.name}`}>×</button>
</li>
))}
</ul>
<div class="kerf-output" style="display: flex; justify-content: space-between; align-items: center;">
<strong>Subtotal</strong>
<strong class="kerf-mono">{fmt(subtotal.value)}</strong>
</div>
<div class="kerf-toolbar">
<button data-action="clear">Clear cart</button>
<button
class="kerf-link-button"
data-action="reset"
style="margin-left: auto;"
>
Reset to initial
</button>
</div>
</div>
));
delegate(root, 'click', '[data-action="remove"]', (_, btn) => {
cart.actions.remove((btn as HTMLElement).dataset.id!);
});
delegate(root, 'click', '[data-action="clear"]', () => cart.actions.clear());
delegate(root, 'click', '[data-action="reset"]', () => cart.reset());