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 AtomRef module provides reactive references that support subscriptions and transformations. Unlike atoms, AtomRefs are simpler value containers optimized for local state management.

Type definitions

ReadonlyRef

interface ReadonlyRef<A> extends Equal.Equal {
  readonly [TypeId]: TypeId
  readonly key: string
  readonly value: A
  readonly subscribe: (f: (a: A) => void) => () => void
  readonly map: <B>(f: (a: A) => B) => ReadonlyRef<B>
}
A read-only reference that can be observed for changes.

AtomRef

interface AtomRef<A> extends ReadonlyRef<A> {
  readonly prop: <K extends keyof A>(prop: K) => AtomRef<A[K]>
  readonly set: (value: A) => AtomRef<A>
  readonly update: (f: (value: A) => A) => AtomRef<A>
}
A mutable reference that can be read, written, and observed.

Collection

interface Collection<A> extends ReadonlyRef<ReadonlyArray<AtomRef<A>>> {
  readonly push: (item: A) => Collection<A>
  readonly insertAt: (index: number, item: A) => Collection<A>
  readonly remove: (ref: AtomRef<A>) => Collection<A>
  readonly toArray: () => Array<A>
}
A collection of AtomRefs with array-like operations.

Creation

make

Create a new AtomRef.
const make: <A>(value: A) => AtomRef<A>
value
A
The initial value
Example
import { AtomRef } from "@effect-rx/rx"

const nameRef = AtomRef.make("Alice")
console.log(nameRef.value) // "Alice"
Example - With object
const userRef = AtomRef.make({
  id: 1,
  name: "Alice",
  email: "alice@example.com"
})

collection

Create a collection of AtomRefs.
const collection: <A>(items: Iterable<A>) => Collection<A>
items
Iterable<A>
Initial items for the collection
Example
const todosRef = AtomRef.collection([
  { id: 1, text: "Buy milk", done: false },
  { id: 2, text: "Walk dog", done: true }
])

console.log(todosRef.value.length) // 2

Reading values

value

Access the current value.
ref.value: A
Example
const countRef = AtomRef.make(42)
console.log(countRef.value) // 42
The value property is a getter that always returns the current value.

Writing values

set

Set a new value.
ref.set(value: A): AtomRef<A>
value
A
The new value
Returns the same ref for chaining. Example
const countRef = AtomRef.make(0)
countRef.set(42)
console.log(countRef.value) // 42
Setting a value that equals the current value (using Equal.equals) will not notify subscribers.

update

Update the value based on its current value.
ref.update(f: (value: A) => A): AtomRef<A>
f
(value: A) => A
Function to compute the new value from the current value
Returns the same ref for chaining. Example
const countRef = AtomRef.make(0)
countRef.update((n) => n + 1)
console.log(countRef.value) // 1

Subscriptions

subscribe

Subscribe to value changes.
ref.subscribe(f: (a: A) => void): () => void
f
(a: A) => void
Callback function called when the value changes
Returns a cleanup function to unsubscribe. Example
const countRef = AtomRef.make(0)

const unsubscribe = countRef.subscribe((count) => {
  console.log("Count changed:", count)
})

countRef.set(1) // Logs: "Count changed: 1"
countRef.set(2) // Logs: "Count changed: 2"

unsubscribe() // Stop listening
Unlike Registry subscriptions, AtomRef subscriptions do not have an immediate option. They only notify on changes.

Transformations

map

Create a derived ref by mapping the value.
ref.map<B>(f: (a: A) => B): ReadonlyRef<B>
f
(a: A) => B
Transform function
Returns a read-only ref with the transformed value. Example
const countRef = AtomRef.make(5)
const doubledRef = countRef.map((n) => n * 2)

console.log(doubledRef.value) // 10

countRef.set(10)
console.log(doubledRef.value) // 20
Example - Subscribe to mapped ref
const userRef = AtomRef.make({ name: "Alice", age: 30 })
const nameRef = userRef.map((user) => user.name)

nameRef.subscribe((name) => {
  console.log("Name:", name)
})

userRef.set({ name: "Bob", age: 30 })
// Logs: "Name: Bob"
Mapped refs only notify subscribers when the transformed value changes (using Equal.equals).

prop

Create a ref focused on a specific property.
ref.prop<K extends keyof A>(prop: K): AtomRef<A[K]>
prop
K
The property key to focus on
Returns a writable ref for that property. Example
const userRef = AtomRef.make({
  name: "Alice",
  email: "alice@example.com"
})

const nameRef = userRef.prop("name")
console.log(nameRef.value) // "Alice"

nameRef.set("Bob")
console.log(userRef.value)
// { name: "Bob", email: "alice@example.com" }
Example - Nested properties
const appRef = AtomRef.make({
  user: {
    profile: {
      name: "Alice"
    }
  }
})

const nameRef = appRef
  .prop("user")
  .prop("profile")
  .prop("name")

nameRef.set("Bob")

Collection operations

push

Add an item to the end of the collection.
collection.push(item: A): Collection<A>
item
A
The item to add
Example
const todosRef = AtomRef.collection([])

todosRef.push({ id: 1, text: "Buy milk", done: false })
todosRef.push({ id: 2, text: "Walk dog", done: false })

console.log(todosRef.value.length) // 2

insertAt

Insert an item at a specific index.
collection.insertAt(index: number, item: A): Collection<A>
index
number
The index to insert at
item
A
The item to insert
Example
const todosRef = AtomRef.collection([
  { id: 1, text: "First", done: false },
  { id: 3, text: "Third", done: false }
])

todosRef.insertAt(1, { id: 2, text: "Second", done: false })
// Collection is now: [First, Second, Third]

remove

Remove an item by reference.
collection.remove(ref: AtomRef<A>): Collection<A>
ref
AtomRef<A>
The ref to remove
Example
const todosRef = AtomRef.collection([
  { id: 1, text: "Buy milk", done: false },
  { id: 2, text: "Walk dog", done: false }
])

const firstTodo = todosRef.value[0]
todosRef.remove(firstTodo)

console.log(todosRef.value.length) // 1

toArray

Get the collection items as a plain array.
collection.toArray(): Array<A>
Example
const todosRef = AtomRef.collection([
  { id: 1, text: "Buy milk", done: false },
  { id: 2, text: "Walk dog", done: true }
])

const todos = todosRef.toArray()
console.log(todos)
// [{ id: 1, ... }, { id: 2, ... }]

Collection subscriptions

Example - Subscribe to collection changes
const todosRef = AtomRef.collection([])

todosRef.subscribe((refs) => {
  console.log("Todos count:", refs.length)
})

todosRef.push({ id: 1, text: "Buy milk", done: false })
// Logs: "Todos count: 1"
Example - Subscribe to individual items
const todosRef = AtomRef.collection([
  { id: 1, text: "Buy milk", done: false }
])

const firstTodo = todosRef.value[0]

firstTodo.subscribe((todo) => {
  console.log("Todo changed:", todo)
})

firstTodo.set({ id: 1, text: "Buy milk", done: true })
// Logs: "Todo changed: { id: 1, text: 'Buy milk', done: true }"
// Also triggers collection subscriber
When an item in a collection changes, both the item’s subscribers and the collection’s subscribers are notified.

Equality

AtomRefs implement Effect’s Equal interface using the value’s equality. Example
import { Equal } from "effect"

const ref1 = AtomRef.make({ id: 1, name: "Alice" })
const ref2 = AtomRef.make({ id: 1, name: "Alice" })

Equal.equals(ref1, ref2) // true (values are equal)

ref1.set({ id: 2, name: "Bob" })
Equal.equals(ref1, ref2) // false (values differ)

Use cases

Form state

const formRef = AtomRef.make({
  email: "",
  password: "",
  rememberMe: false
})

const emailRef = formRef.prop("email")
const passwordRef = formRef.prop("password")

// In a component
emailRef.set("user@example.com")
passwordRef.set("secret")

console.log(formRef.value)
// { email: "user@example.com", password: "secret", rememberMe: false }

Todo list

interface Todo {
  id: number
  text: string
  done: boolean
}

const todosRef = AtomRef.collection<Todo>([])

function addTodo(text: string) {
  todosRef.push({
    id: Date.now(),
    text,
    done: false
  })
}

function toggleTodo(todoRef: AtomRef<Todo>) {
  todoRef.update((todo) => ({
    ...todo,
    done: !todo.done
  }))
}

function removeTodo(todoRef: AtomRef<Todo>) {
  todosRef.remove(todoRef)
}

Counter with history

const countRef = AtomRef.make(0)
const historyRef = AtomRef.collection<number>([])

countRef.subscribe((count) => {
  historyRef.push(count)
})

countRef.set(1)
countRef.set(2)
countRef.set(3)

console.log(historyRef.toArray())
// [1, 2, 3]

Build docs developers (and LLMs) love