Atoms
Factory
import { atom, atomCollection } from "@mongez/react-atom";
const counterAtom = atom({ key: "counter", default: 0 });const todosAtom = atomCollection<Todo>({ key: "todos", default: [] });Naming convention: suffix atom variables with
Atom(e.g.counterAtom,userAtom). This prevents name collisions with local variables and plain values, and makes it immediately clear at the call site that you’re working with a reactive atom.
atom(...) is the React-aware version of createAtom from @mongez/atom. The returned atom carries all the base methods plus the React hooks below.
The hooks every atom carries
useValue()
Subscribes to changes; returns the current value. Re-renders when the value changes.
function Greeting() { const user = userAtom.useValue(); return <h1>Hello {user.name}</h1>;}useState() — available but not recommended
Returns [value, setValue] — same shape as React’s useState, but the value lives in the atom.
function Counter() { const [count, setCount] = counterAtom.useState(); return <button onClick={() => setCount(count + 1)}>{count}</button>;}The setter accepts an updater function: setCount(prev => prev + 1).
Prefer useValue() + calling the atom’s methods directly — it keeps updates explicit and traceable:
// ✅ preferredfunction Counter() { const count = counterAtom.useValue(); return <button onClick={() => counterAtom.update(count + 1)}>{count}</button>;}
// Or better — give the increment semantic meaning via an action:const counterAtom = atom({ key: "counter", default: 0, actions: { increment() { this.update(this.value + 1); } },});
function Counter() { const count = counterAtom.useValue(); return <button onClick={() => counterAtom.increment()}>{count}</button>;}use(key)
For object atoms: subscribes to ONE key only. Re-renders only when THAT key changes — sibling keys in the same atom don’t wake this component.
function NameOnly() { const name = userAtom.use("name"); return <span>{name}</span>;}// Even if userAtom.merge({ age: 31 }), this component doesn't re-render.useWatch(key, callback)
Effect-style watcher. Use when you want a side effect (analytics, logging) without re-rendering. useWatch is itself a hook — call it at the top of your component, NOT inside a useEffect.
function NameTracker() { userAtom.useWatch("name", (next, prev) => analytics.track("name_changed", { from: prev, to: next }), ); return null;}Provider
A React component that sets the atom’s value on mount and updates it when value prop changes.
<userAtom.Provider value={initialUser}> <App /></userAtom.Provider>Useful for testing (inject a known value into the subtree) and for “set on mount” patterns.
Custom actions in React atoms
Action methods declared in the factory still work — they’re bound to the atom instance, available globally.
const auth = atom<AuthState, { login: (creds: Creds) => Promise<void> }>({ key: "auth", default: { user: null }, actions: { async login(creds) { const user = await api.login(creds); this.merge({ user }); }, },});
// Anywhere:await auth.login({ email, password });
// In components:const user = auth.use("user");With store scoping (SSR)
function Component() { // `useAtom(template)` resolves to the active store's scoped clone. // Without a provider, it returns the template itself (singleton mode). const scoped = useAtom(userAtom); const value = scoped.useValue(); // tear-free read return <span>{value.name}</span>;}The per-atom hooks (useValue, useState, use, useWatch) ALREADY do this resolution internally — you don’t need to call useAtom(template) explicitly unless you’re invoking action methods on the scoped clone from an event handler. See ../ssr/SKILL.md.