selectorFamily
selectorFamily<K, T>(factory: (key: K) => (get: GetFn, options: SelectorGetOptions) => T): SelectorFamily<K, T>valdres Create a keyed collection of derived selectors
Creates a family of selectors keyed by a parameter. Each selector derives its value from atoms or other selectors based on the key.
The family object is a factory, not readable or subscribable state. Read or
subscribe to a member such as userDisplayNameSelector("user-1"). Unlike an
atomFamily, creating a selector-family member does not add store-local state
or membership to enumerate.
Selector options apply to every family member. In particular, use
{ mutable: true } when results contain mutable built-ins or host objects; see
the selector immutability contract.
Member identity and lifetime
A member's identity is stable while live: repeated calls with the same key return the same selector while a caller or live store strongly retains it, such as through a subscription, dependency edge, or an enumerable store's cold cache. A default non-enumerable store uses weak keys for cold entries, so a cold read there does not by itself keep a member alive. The family cache is weak; after a member becomes unreachable, garbage collection may reclaim it and a later call can create a fresh identity.
release(...args) explicitly evicts the cached lookup early. Existing callers
and stores may continue using the released selector, while a later family call
can create a new member for those arguments.
Each member getter also receives SelectorGetOptions. Its signal is aborted
when that evaluation is superseded or disposed, and storeId identifies the
store evaluating the member. Existing getters that only accept get remain
valid.
const userSelector = selectorFamily(
userId =>
(get, { signal, storeId }) =>
fetch(`/api/users/${userId}?store=${storeId}`, { signal }).then(
res => res.json(),
),
)
Usage
import { selectorFamily } from "valdres"
import { userAtom } from "./userAtom"
const userDisplayNameSelector = selectorFamily(id => get => {
const { firstName, lastName } = get(userAtom(id))
return `${firstName} ${lastName}`
})
store.get(userDisplayNameSelector("user-1")) // "John Doe"
Custom key identity
Selector families use the same deterministic key codec as atomFamily: types
and argument count are distinct, while plain Object keys and Map/Set contents
are canonicalized independent of insertion order. Unsupported values (including
Symbols, functions, Promises, class instances, and cycles) throw a TypeError.
Provide keyOf when the original argument is unsupported or when only part of
it should determine selector identity:
const userRevision = selectorFamily(
user => get => `${get(userAtom(user.id)).name}@${user.revision}`,
{ keyOf: user => [user.id, user.revision] },
)
keyOf is also the lever for read-heavy code. A single string or number
argument is looked up directly, but an object argument has to be canonicalized
on every call — family({ id }) in a loop re-encodes that object each time,
while family(id) does not. If a family with structured arguments sits on a hot
path, give it a keyOf that projects the identifying part:
// re-encodes { id, revision } on every call
const slow = selectorFamily(user => get => get(userAtom(user.id)).name)
// encodes one string instead
const fast = selectorFamily(user => get => get(userAtom(user.id)).name, {
keyOf: user => user.id,
})
With async computation
const userPostsSelector = selectorFamily(userId => get =>
fetch(`/api/users/${userId}/posts`).then(res => res.json()),
)
As with selector(), the member getter itself must be synchronous. It may
return a Promise, but a native async function is rejected because Valdres
unwraps settled selector values and therefore cannot preserve an async
function's always-Promise return contract.
In React
import { useValue } from "valdres-react"
function UserName({ userId }) {
const displayName = useValue(userDisplayNameSelector(userId))
return <span>{displayName}</span>
}
See also
- selector — create a single selector
- atomFamily — create a family of atoms
- index — reactively filter an atom family by a term