atomFamily

Signature
atomFamily<K, T>(defaultFactory?: (key: K) => T, options?: AtomFamilyOptions): AtomFamily<K, T>

valdres Create a keyed collection of atoms

Creates a family of atoms keyed by a parameter. Useful for collections of entities like users, todos, or items where each instance needs its own piece of state.

Usage

import { atomFamily } from "valdres"

// Simple family with no default
const todoAtom = atomFamily()

store.set(todoAtom("abc-123"), { title: "Buy milk", done: false })
store.get(todoAtom("abc-123")) // { title: "Buy milk", done: false }

With a default value factory

Pass a function that receives the key and returns the default value:

const userAtom = atomFamily(id => ({
    id,
    name: "",
    email: "",
}))

// The default is generated when first accessed
store.get(userAtom("user-1")) // { id: "user-1", name: "", email: "" }

Global families

Use globalAtomFamily to share each member's value across stores. options.name is required — a global family is addressed by it, so omitting it is a type error and remains a runtime error for untyped JavaScript.

import { globalAtomFamily } from "valdres"

const featureFlagAtom = globalAtomFamily((key: string) => false, {
    name: "app/feature-flags",
})

Re-defining a family under the same name returns the existing family instead of creating a second one. Selectors do not have a global counterpart.

Key identity

Family calls are keyed by a deterministic structural encoding. Primitive types, argument count, Arrays, plain Objects, Dates, Maps, and Sets remain distinct; object property order and Map/Set insertion order do not affect identity. BigInt is supported too.

Values without deterministic structural semantics—such as Symbols, functions, Promises, class instances, accessor properties, and cyclic structures—throw a TypeError. Use keyOf to derive a supported identity for those arguments, or to intentionally group multiple arguments:

type Entity = { id: string; self?: Entity }

const entityAtom = atomFamily<Entity, [Entity]>(entity => entity, {
    keyOf: entity => entity.id,
})

keyOf receives the same argument tuple as the family factory. Its result is run through the same canonical codec.

Transferred families need JSON-safe args
Key identity is wider than JSON on purpose — it is a local, in-process concern. But dehydrate emits family args raw (schemas encode values, not keys) and hydrate re-derives each member with family(...args) from the parsed payload. A Date, Map, Set, BigInt, NaN or undefined argument does not survive that round-trip, so the value would land on a phantom member while the real one keeps its default. Dev builds throw from dehydrate, naming the family and the argument path. Key transferred families by strings and numbers (user(id), day(date.toISOString())) — keyOf does not help here, since the raw args are what crosses the wire.

For the same reason, a named family's keyOf must derive its key from the arguments' JSON data (entity => entity.id). JSON carries no property descriptors, no frozen/sealed flag, and no object identity — two references to one object arrive as two objects — so a keyOf reading any of those returns a different key after hydration. dehydrate cannot detect this and does not try.

The legacy release(...args) method is deprecated and has no effect. Family members leave the weak identity cache automatically once no caller or store can reach them. Manual eviction could otherwise create two live member objects for the same arguments and split their values across stores.

Subscribing to a family

Tip
Subscribe to an entire family to be notified when any member changes. The callback receives that member's family arguments.

store.sub(todoAtom, id => {
    console.log("Changed todo ID:", id)
})

In React

import { useValue } from "valdres-react"

function UserProfile({ userId }) {
    const user = useValue(userAtom(userId))
    return <div>{user.name}</div>
}

See also

  • atom — create a single atom
  • globalAtomFamily — share each member's value across every store
  • selectorFamily — derive state from a family of atoms
  • index — reactively filter family members by a term