selector
selector<T>(getter: (get: GetFn) => T): Selector<T>valdres Derive computed state that updates automatically
Creates derived state that automatically recomputes when its dependencies change. Selectors are read-only — they compute their value from atoms and other selectors.
Usage
import { atom, selector } from "valdres"
const firstNameAtom = atom("John")
const lastNameAtom = atom("Doe")
const fullNameSelector = selector(get => {
const first = get(firstNameAtom)
const last = get(lastNameAtom)
return `${first} ${last}`
})
Parameters
| Parameter | Type | Description |
|---|---|---|
getter | (get: GetFn) => T | A function that computes the derived value. Use get() to read atoms or other selectors. |
options.name | string | Optional name for debugging, devtools, and validation error messages |
options.schema | Schema<T> | Schema for runtime validation of the selector's result, and type inference. A no-op unless validation is enabled. See Schema Validation. |
options.schemaValidation | boolean | Per-selector override of the store's schemaValidation flag — true always validates this selector, false exempts it |
options.mutable | boolean | Opt out of development/test deep-freezing. Required when results contain mutable built-ins or host objects that cannot be frozen safely. |
Selectors follow the same immutability and exotic-value
contract as atoms. When a
selector returns a value from a mutable atom unchanged, mark the selector
mutable: true as well so its cache does not attempt to deep-freeze that value.
Async selectors
Selectors can return a promise. They work with Suspense in React. Note that
the getter itself must be a sync function that returns a promise — selector()
rejects async functions at creation time:
const userDataSelector = selector(get => {
const userId = get(userIdAtom)
return fetch(`/api/users/${userId}`).then(res => res.json())
})
Schema validation
A selector's schema validates its computed result on every evaluation — a
runtime contract that catches a selector drifting from its declared shape:
const fullNameSelector = selector(
get => `${get(firstNameAtom)} ${get(lastNameAtom)}`,
{ name: "fullName", schema: z.string() },
)
Validation is opt-in per store (store({ schemaValidation: true })) or per
selector (schemaValidation: true). See the
Schema Validation guide.
Evaluation errors
Synchronous exceptions thrown while evaluating a selector are wrapped in
SelectorEvaluationError. The original exception remains available as
error.cause, and the message includes the named selector chain that led to
the failure. Give selectors a name to make that trace useful.
A dependency cycle throws SelectorCircularDependencyError, which extends
SelectorEvaluationError. Both classes are exported from valdres, so check
for the more specific circular error first when handling both:
import {
SelectorCircularDependencyError,
SelectorEvaluationError,
} from "valdres"
try {
appStore.get(profileSelector)
} catch (error) {
if (error instanceof SelectorCircularDependencyError) {
console.error("Selector dependency cycle", error.message)
} else if (error instanceof SelectorEvaluationError) {
console.error("Selector evaluation failed", error.cause)
} else {
throw error
}
}
See also
- selectorFamily — create a collection of selectors keyed by a parameter
- Schema Validation — runtime validation and type inference
- fromState — read a selector value in your components