Family
atomFamily
:::caution Deprecated
atomFamily is deprecated and will be removed in Jotai v3.
Please migrate to the jotai-family package, which provides the same API with additional features like atomTree.
Migration:
npm install jotai-family
// Beforeimport { atomFamily } from 'jotai/utils'// Afterimport { atomFamily } from 'jotai-family'
The API is identical, so no code changes are needed beyond the import statement. :::
Ref: https://github.com/pmndrs/jotai/issues/23
Usage
atomFamily(initializeAtom, areEqual): (param) => Atom
This will create a function that takes param and returns an atom.
If the atom has already been created, it will be returned from the cache.
initializeAtom is a function that can return any kind of atom (atom(), atomWithDefault(), ...).
Note that the areEqual argument is optional and compares
if two params are equal (defaults to Object.is).
To reproduce behavior similar to Recoil's atomFamily/selectorFamily,
specify a deepEqual function to areEqual. For example:
import { atom } from 'jotai'import { atomFamily } from 'jotai/utils'import deepEqual from 'fast-deep-equal'const fooFamily = atomFamily((param) => atom(param), deepEqual)
TypeScript
The atom family types will be inferred from initializeAtom. Here's a typical usage with a primitive atom.
import type { PrimitiveAtom } from 'jotai'/*** here the atom(id) returns a PrimitiveAtom<number>* and PrimitiveAtom<number> is a WritableAtom<number, SetStateAction<number>>*/const myFamily = atomFamily((id: number) => atom(id))
You can explicitly declare the type of parameter and atom type using TypeScript generics.
atomFamily<Param, AtomType extends Atom<unknown>>(initializeAtom: (param: Param) => AtomType,areEqual?: (a: Param, b: Param) => boolean): AtomFamily<Param, AtomType>
Example with explicit types:
import { atom } from 'jotai'import type { PrimitiveAtom } from 'jotai'import { atomFamily } from 'jotai/utils'const myFamily = atomFamily<number, PrimitiveAtom<number>>((id: number) =>atom(id),)
API Methods
The atomFamily function returns an object with the following methods:
myFamily(param)
Returns an atom for the given param. If the atom has already been created, it will be returned from the cache.
myFamily.getParams()
Returns an iterable of all params currently in the cache.
const todoFamily = atomFamily((name) => atom(name))todoFamily('foo')todoFamily('bar')for (const param of todoFamily.getParams()) {console.log(param) // 'foo', 'bar'}
myFamily.remove(param)
Removes a specific param from the cache.
todoFamily.remove('foo')
myFamily.setShouldRemove(shouldRemove)
Registers a shouldRemove function which runs immediately and when you are about to get an atom from the cache.
shouldRemoveis a function that takes two arguments:createdAt(in milliseconds) andparam, and returns a boolean value.- Setting
nullwill remove the previously registered function.
// Remove atoms older than 1 hourtodoFamily.setShouldRemove((createdAt, param) => {return Date.now() - createdAt > 60 * 60 * 1000})
myFamily.unstable_listen(callback)
⚠️ Unstable API: This API is for advanced use cases and can change without notice.
Fires when an atom is created or removed. Returns a cleanup function.
const cleanup = todoFamily.unstable_listen((event) => {console.log(event.type) // 'CREATE' or 'REMOVE'console.log(event.param) // the paramconsole.log(event.atom) // the atom})// Later, stop listeningcleanup()
Caveat: Memory Leaks
Internally, atomFamily is just a Map whose key is a param and whose value is an atom config. Unless you explicitly remove unused params, this leads to memory leaks. This is crucial if you use infinite number of params.
Use myFamily.remove(param) or myFamily.setShouldRemove(shouldRemove) to manage memory.
Examples
import { atom } from 'jotai'import { atomFamily } from 'jotai/utils'const todoFamily = atomFamily((name) => atom(name))todoFamily('foo')// this will create a new atom('foo'), or return the one if already created
import { atom } from 'jotai'import { atomFamily } from 'jotai/utils'const todoFamily = atomFamily((name) =>atom((get) => get(todosAtom)[name],(get, set, arg) => {const prev = get(todosAtom)set(todosAtom, { ...prev, [name]: { ...prev[name], ...arg } })},),)
import { atom } from 'jotai'import { atomFamily } from 'jotai/utils'const todoFamily = atomFamily(({ id, name }) => atom({ name }),(a, b) => a.id === b.id,)
Stackblitz
Migration to jotai-family
For new projects or when updating existing code, we recommend using the jotai-family package instead. It provides:
- Same API: Drop-in replacement for
atomFamily - Additional features: Includes
atomTreefor hierarchical atom management - Better maintenance: Dedicated package with focused development
- Future-proof: Will continue to be supported in Jotai v3 and beyond
See the jotai-family documentation for more details.