Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/reatom/reatom/llms.txt

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

Overview

A computed value is a derived state container that automatically tracks its dependencies and recalculates only when those dependencies change. Computed values are lazy - they only run when read AND subscribed to. Computed values:
  • Automatically track which atoms they depend on
  • Only recalculate when dependencies change
  • Cache their result until dependencies update
  • Are read-only (cannot be set directly)

Creating Computed Values

Basic Usage

Create a computed value with a function:
import { atom, computed } from '@reatom/core'

const count = atom(5, 'count')
const doubled = computed(() => count() * 2, 'doubled')

// Reading triggers computation (if subscribed)
const value = doubled() // -> 10

Type Signature

interface Computed<State = any> {
  // Read the computed value
  (): State
  
  // Subscribe to changes
  subscribe(cb?: (state: State) => any): Unsubscribe
  
  // Extension system
  extend: Extend<this>
  
  // No set method - computed values are read-only
}

Automatic Dependency Tracking

Computed values automatically track any atoms read during execution:
const firstName = atom('John', 'firstName')
const lastName = atom('Doe', 'lastName')

// Automatically depends on firstName and lastName
const fullName = computed(() => {
  return `${firstName()} ${lastName()}`
}, 'fullName')

fullName.subscribe(name => console.log(name))
// Logs: "John Doe"

firstName.set('Jane')
// Logs: "Jane Doe" - automatically recalculated
You don’t need to declare dependencies manually - Reatom tracks them automatically!

Lazy Evaluation

Computed values are lazy - they only execute when:
  1. They are read (called as a function)
  2. AND they have subscribers OR are dirty
const count = atom(0, 'count')

let computeCount = 0
const doubled = computed(() => {
  computeCount++
  console.log('Computing doubled...')
  return count() * 2
}, 'doubled')

// No computation yet - not subscribed
count.set(5)
console.log(computeCount) // -> 0

// Subscribe triggers initial computation
const unsub = doubled.subscribe()
// Logs: "Computing doubled..."
console.log(computeCount) // -> 1

// Changes trigger recomputation
count.set(10)
// Logs: "Computing doubled..."
console.log(computeCount) // -> 2

Conditional Dependencies

Dependencies can be conditional based on runtime logic:
const isActive = atom(true, 'isActive')
const dataA = atom('A', 'dataA')
const dataB = atom('B', 'dataB')

const result = computed(() => {
  // Dependencies change based on isActive
  if (isActive()) {
    return dataA() // Only depends on dataA when active
  } else {
    return dataB() // Only depends on dataB when inactive
  }
}, 'result')

Chaining Computed Values

Computed values can depend on other computed values:
const count = atom(5, 'count')
const doubled = computed(() => count() * 2, 'doubled')
const quadrupled = computed(() => doubled() * 2, 'quadrupled')

quadrupled.subscribe(value => console.log(value))
// Logs: 20

count.set(10)
// Logs: 40 - both computed values update
Reatom efficiently handles dependency chains. Only the necessary computations run when an atom changes.

Derived State Patterns

Filtering

const todos = atom([
  { id: 1, text: 'Learn Reatom', done: false },
  { id: 2, text: 'Build app', done: true },
], 'todos')

const activeTodos = computed(() => {
  return todos().filter(todo => !todo.done)
}, 'activeTodos')

const completedCount = computed(() => {
  return todos().filter(todo => todo.done).length
}, 'completedCount')

Transformation

const users = atom([
  { id: 1, name: 'Alice', age: 30 },
  { id: 2, name: 'Bob', age: 25 },
], 'users')

const userNames = computed(() => {
  return users().map(u => u.name)
}, 'userNames')

const averageAge = computed(() => {
  const list = users()
  return list.reduce((sum, u) => sum + u.age, 0) / list.length
}, 'averageAge')

Aggregation

const itemsAtom = atom([1, 2, 3, 4, 5], 'items')

const sum = computed(() => {
  return itemsAtom().reduce((a, b) => a + b, 0)
}, 'sum')

const average = computed(() => {
  const items = itemsAtom()
  return sum() / items.length
}, 'average')

Error Handling

Computed values can handle errors in their dependencies:
const data = atom<number>(5, 'data')

const riskyComputed = computed(() => {
  const value = data()
  if (value < 0) {
    throw new Error('Value cannot be negative')
  }
  return value * 2
}, 'riskyComputed')

const safeComputed = computed(() => {
  try {
    return riskyComputed()
  } catch (error) {
    console.error('Error in computation:', error)
    return 0 // Fallback value
  }
}, 'safeComputed')

Disconnecting Dependencies

When a computed value is unsubscribed, it automatically disconnects from its dependencies:
import { atom, computed, isConnected } from '@reatom/core'

const source = atom(0, 'source')
const derived = computed(() => source(), 'derived')

console.log(isConnected(source)) // false

const unsub = derived.subscribe()
console.log(isConnected(source)) // true - source is connected

unsub()
console.log(isConnected(source)) // false - automatically disconnected

Comparison with Atoms

FeatureAtomComputed
Can be setYesNo
Has dependenciesNoYes
Tracks dependentsYesYes
CachingAlways currentCached until deps change
Use caseMutable stateDerived state

Performance Optimization

Memoization

Computed values automatically memoize their results:
const expensiveData = atom([], 'expensiveData')

const processed = computed(() => {
  console.log('Processing...')
  return expensiveProcessing(expensiveData())
}, 'processed')

processed.subscribe()

// Multiple reads use cached value
processed() // Logs: "Processing..."
processed() // No log - uses cached value
processed() // No log - uses cached value

// Only recomputes when dependency changes
expensiveData.set([1, 2, 3])
// Logs: "Processing..." - recalculated

Avoiding Over-computation

const data = atom([1, 2, 3], 'data')

// Computed on every read of parent
const result = computed(() => {
  return data().map(x => {
    return computed(() => x * 2) // Don't create computed inside computed!
  })
}, 'result')

Best Practices

Computed functions should be pure - no side effects:
// Good - pure function
const doubled = computed(() => count() * 2, 'doubled')

// Avoid - side effects
const withSideEffect = computed(() => {
  console.log('Computing...') // Side effect!
  return count() * 2
}, 'withSideEffect')
Name computed values based on what they represent:
// Good
const completedTasksCount = computed(...)
const formattedDate = computed(...)

// Avoid
const computed1 = computed(...)
const temp = computed(...)
Remember that computed values are lazy - they won’t run unless subscribed:
const expensive = computed(() => {
  // This won't run until something subscribes!
  return heavyProcessing()
}, 'expensive')

// Need to subscribe for it to react
expensive.subscribe()

Atom

Create mutable state containers

Actions

Encapsulate logic and side effects

Effects

Run reactive side effects

Extend

Add capabilities with extensions