atomFamily

Signature
atomFamily<K, T>(defaultFactory?: (key: K) => T): 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: "" }

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.

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
  • selectorFamily — derive state from a family of atoms
  • index — reactively filter family members by a term