Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tim-smart/effect-atom/llms.txt

Use this file to discover all available pages before exploring further.

The Registry module provides a container for managing atom state and subscriptions. It’s responsible for computing atom values, tracking dependencies, and notifying subscribers of changes.

Type definitions

Registry

interface Registry {
  readonly [TypeId]: TypeId
  readonly getNodes: () => ReadonlyMap<Atom.Atom<any> | string, Node<any>>
  readonly get: <A>(atom: Atom.Atom<A>) => A
  readonly mount: <A>(atom: Atom.Atom<A>) => () => void
  readonly refresh: <A>(atom: Atom.Atom<A>) => void
  readonly set: <R, W>(atom: Atom.Writable<R, W>, value: W) => void
  readonly setSerializable: (key: string, encoded: unknown) => void
  readonly modify: <R, W, A>(atom: Atom.Writable<R, W>, f: (_: R) => [returnValue: A, nextValue: W]) => A
  readonly update: <R, W>(atom: Atom.Writable<R, W>, f: (_: R) => W) => void
  readonly subscribe: <A>(atom: Atom.Atom<A>, f: (_: A) => void, options?: {
    readonly immediate?: boolean
  }) => () => void
  readonly reset: () => void
  readonly dispose: () => void
}
The registry manages atom state and provides methods for reading, writing, and subscribing to atoms.

Creation

make

Create a new Registry instance.
const make: (options?: {
  readonly initialValues?: Iterable<readonly [Atom.Atom<any>, any]> | undefined
  readonly scheduleTask?: ((f: () => void) => void) | undefined
  readonly timeoutResolution?: number | undefined
  readonly defaultIdleTTL?: number | undefined
}) => Registry
options.initialValues
Iterable<[Atom, value]>
Initial values to hydrate atoms with
options.scheduleTask
(f: () => void) => void
Custom task scheduler for batching updates. Defaults to immediate execution.
options.timeoutResolution
number
Resolution in milliseconds for idle TTL checks. Defaults to 1000.
options.defaultIdleTTL
number
Default idle TTL in milliseconds for all atoms. Atoms are disposed after this time if they have no subscribers.
Example
import { Registry } from "@effect-rx/rx"

const registry = Registry.make()
Example - With initial values
const registry = Registry.make({
  initialValues: [
    [userAtom, { id: 1, name: "Alice" }],
    [themeAtom, "dark"]
  ]
})
Example - With React integration
import { startTransition } from "react"

const registry = Registry.make({
  scheduleTask: (f) => startTransition(f)
})

Reading atoms

get

Get the current value of an atom.
registry.get<A>(atom: Atom.Atom<A>): A
atom
Atom<A>
The atom to read
Example
const count = registry.get(countAtom)
console.log(count) // 42
Reading an atom computes its value if it hasn’t been computed yet, and tracks it as a dependency of the current atom context (if any).

Writing atoms

set

Set the value of a writable atom.
registry.set<R, W>(atom: Atom.Writable<R, W>, value: W): void
atom
Writable<R, W>
The writable atom
value
W
The new value
Example
registry.set(countAtom, 42)
registry.set(userAtom, { id: 1, name: "Alice" })

update

Update an atom’s value based on its current value.
registry.update<R, W>(atom: Atom.Writable<R, W>, f: (_: R) => W): void
atom
Writable<R, W>
The writable atom
f
(current: R) => W
Function to compute new value from current value
Example
registry.update(countAtom, (n) => n + 1)

modify

Modify an atom and return a value.
registry.modify<R, W, A>(
  atom: Atom.Writable<R, W>,
  f: (_: R) => [returnValue: A, nextValue: W]
): A
atom
Writable<R, W>
The writable atom
f
function
Function that returns both a return value and the next atom value
Example
const previousCount = registry.modify(
  countAtom,
  (n) => [n, n + 1]
)
console.log(previousCount) // 42
console.log(registry.get(countAtom)) // 43

setSerializable

Set a serializable atom’s value using its serialization key.
registry.setSerializable(key: string, encoded: unknown): void
key
string
The serialization key of the atom
encoded
unknown
The encoded value
Example
// Hydrate from server data
const serverData = {
  "user-preferences": { theme: "dark", locale: "en" }
}

for (const [key, value] of Object.entries(serverData)) {
  registry.setSerializable(key, value)
}

Subscriptions

subscribe

Subscribe to changes in an atom’s value.
registry.subscribe<A>(
  atom: Atom.Atom<A>,
  f: (_: A) => void,
  options?: { readonly immediate?: boolean }
): () => void
atom
Atom<A>
The atom to subscribe to
f
(value: A) => void
Callback function called when the value changes
options.immediate
boolean
default:"false"
If true, calls the callback immediately with the current value
Returns a cleanup function to unsubscribe. Example
const unsubscribe = registry.subscribe(
  countAtom,
  (count) => console.log("Count:", count)
)

registry.set(countAtom, 42) // Logs: "Count: 42"

unsubscribe() // Stop listening
Example - Immediate callback
registry.subscribe(
  countAtom,
  (count) => console.log("Count:", count),
  { immediate: true }
)
// Immediately logs current value

mount

Mount an atom to keep it alive even without subscribers.
registry.mount<A>(atom: Atom.Atom<A>): () => void
atom
Atom<A>
The atom to mount
Returns a cleanup function to unmount the atom. Example
const unmount = registry.mount(backgroundTaskAtom)

// Atom stays active even with no subscribers
// ...

unmount() // Allow disposal

Lifecycle

refresh

Force an atom to recompute its value.
registry.refresh<A>(atom: Atom.Atom<A>): void
atom
Atom<A>
The atom to refresh
Example
// Manually refresh data
registry.refresh(userAtom)

reset

Reset the registry, clearing all atom state.
registry.reset(): void
Example
// Clear all cached state
registry.reset()
This clears all atom values and resets them to their initial state. Subscribers remain active.

dispose

Dispose of the registry and all its atoms.
registry.dispose(): void
Example
// Clean up when done
registry.dispose()
After disposal, the registry cannot be used. Create a new registry if needed.

Debugging

getNodes

Get all active atom nodes for debugging.
registry.getNodes(): ReadonlyMap<Atom.Atom<any> | string, Node<any>>
Example
const nodes = registry.getNodes()
console.log("Active atoms:", nodes.size)

for (const [atom, node] of nodes) {
  console.log("Atom:", atom)
  console.log("Value:", node.value())
}

Effect integration

AtomRegistry

Effect Tag for accessing the current registry.
class AtomRegistry extends Context.Tag("@effect/atom/Registry/CurrentRegistry")<
  AtomRegistry,
  Registry
>() {}
Example
import { Effect } from "effect"
import { Registry } from "@effect-rx/rx"

const program = Effect.gen(function* () {
  const registry = yield* Registry.AtomRegistry
  const count = registry.get(countAtom)
  return count
})

layer

Default Layer for providing a Registry.
const layer: Layer.Layer<AtomRegistry>
Example
import { Effect, Layer } from "effect"

const program = Effect.gen(function* () {
  const registry = yield* Registry.AtomRegistry
  return registry.get(countAtom)
})

const runnable = Effect.provide(program, Registry.layer)

layerOptions

Create a custom Layer with options.
const layerOptions: (options?: {
  readonly initialValues?: Iterable<readonly [Atom.Atom<any>, any]> | undefined
  readonly scheduleTask?: ((f: () => void) => void) | undefined
  readonly timeoutResolution?: number | undefined
  readonly defaultIdleTTL?: number | undefined
}) => Layer.Layer<AtomRegistry>
Example
const customLayer = Registry.layerOptions({
  defaultIdleTTL: 60000, // 1 minute
  timeoutResolution: 5000 // Check every 5 seconds
})

const program = Effect.provide(myProgram, customLayer)

Stream conversions

toStream

Convert an atom to a Stream of its values.
const toStream: {
  <A>(atom: Atom.Atom<A>): (self: Registry) => Stream.Stream<A>
  <A>(self: Registry, atom: Atom.Atom<A>): Stream.Stream<A>
}
Example
const countStream = Registry.toStream(registry, countAtom)

Stream.runForEach(countStream, (count) =>
  Console.log(`Count: ${count}`)
)

toStreamResult

Convert a Result atom to a Stream that emits success values and fails on errors.
const toStreamResult: {
  <A, E>(atom: Atom.Atom<Result.Result<A, E>>): (self: Registry) => Stream.Stream<A, E>
  <A, E>(self: Registry, atom: Atom.Atom<Result.Result<A, E>>): Stream.Stream<A, E>
}
Example
const userStream = Registry.toStreamResult(registry, userAtom)

Stream.runForEach(userStream, (user) =>
  Console.log(`User: ${user.name}`)
)

getResult

Get a Result atom’s value as an Effect.
const getResult: {
  <A, E>(atom: Atom.Atom<Result.Result<A, E>>, options?: {
    readonly suspendOnWaiting?: boolean | undefined
  }): (self: Registry) => Effect.Effect<A, E>
  <A, E>(self: Registry, atom: Atom.Atom<Result.Result<A, E>>, options?: {
    readonly suspendOnWaiting?: boolean | undefined
  }): Effect.Effect<A, E>
}
options.suspendOnWaiting
boolean
default:"false"
If true, the Effect suspends while the result is in a waiting state
Example
const getUserEffect = Registry.getResult(registry, userAtom)

const program = Effect.gen(function* () {
  const user = yield* getUserEffect
  console.log(user.name)
})

Type guards

isRegistry

Check if a value is a Registry.
const isRegistry: (u: unknown) => u is Registry
Example
if (Registry.isRegistry(value)) {
  const count = value.get(countAtom)
}

Build docs developers (and LLMs) love