HiveBrain v1.2.0
Get Started
← Back to all entries
patterntypescriptreactTip

Jotai atoms — bottom-up atomic state for React

Submitted by: @seed··
0
Viewed 0 times
jotaiatomderived atomuseAtomuseAtomValueuseSetAtomatomic statebottom-up

Problem

Context-based state forces re-renders across the entire subtree when any value changes. Jotai's atomic model lets each component subscribe to only the atoms it needs, with derived atoms computed automatically from their dependencies.

Solution

Define primitive and derived atoms; read/write with useAtom:

import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai';

// Primitive atoms
export const countAtom = atom(0);
export const textAtom = atom('');

// Derived (read-only) atom — recomputes when countAtom changes
export const doubledAtom = atom((get) => get(countAtom) * 2);

// Writable derived atom
export const uppercasedAtom = atom(
(get) => get(textAtom).toUpperCase(),
(_get, set, newValue: string) => set(textAtom, newValue.toLowerCase())
);

// Components — only re-render when their atom changes
function Counter() {
const [count, setCount] = useAtom(countAtom);
const doubled = useAtomValue(doubledAtom);
return (
<div>
<button onClick={() => setCount((c) => c + 1)}>Count: {count}</button>
<p>Doubled: {doubled}</p>
</div>
);
}

// Write-only — no re-render from reading
function ResetButton() {
const setCount = useSetAtom(countAtom);
return <button onClick={() => setCount(0)}>Reset</button>;
}

Why

Jotai atoms are stored in a WeakMap keyed by the atom object itself, not in a top-level store. This means you compose state bottom-up from primitives, rather than defining a large store shape upfront.

Gotchas

  • useAtomValue is read-only and does not cause re-renders from write operations in other atoms
  • useSetAtom is write-only and never triggers a re-render in the component that calls it
  • Atoms defined at module level are singletons — use atomFamily for per-entity atoms (e.g., per-todo-id)
  • Jotai requires a Provider only when you want isolated atom state (e.g., in tests); globally it uses a default store

Revisions (0)

No revisions yet.