docs/utilities/family.mdx
:::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
// Before
import { atomFamily } from 'jotai/utils'
// After
import { 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
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)
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),
)
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.
shouldRemove is a function that takes two arguments: createdAt (in milliseconds) and param, and returns a boolean value.null will remove the previously registered function.// Remove atoms older than 1 hour
todoFamily.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 param
console.log(event.atom) // the atom
})
// Later, stop listening
cleanup()
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.
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,
)
For new projects or when updating existing code, we recommend using the jotai-family package instead. It provides:
atomFamilyatomTree for hierarchical atom managementSee the jotai-family documentation for more details.