computed()

Declares a computed (derived) field inside a model. The value is lazily evaluated from other state.

import { computed } from 'comwit'

Basic usage

import { model, computed } from 'comwit'

const cart = model({
  items: [] as CartItem[],
  total: computed((state) => state.items.reduce((sum, i) => sum + i.price * i.qty, 0)),
  isEmpty: computed((state) => state.items.length === 0),
})

No special type needed — computed fields are typed by the selector return value:

type CartState = {
  items: CartItem[]
  total: number
  isEmpty: boolean
}

How it works

  • Lazy — evaluated on every access, not eagerly cached
  • Reactive — returns the latest value when dependencies change
  • Read-only — throws Cannot set computed field "..." on mutation attempt
  • Composable — computed fields can depend on other computed fields
const stats = model({
  count: 3,
  doubled: computed((state) => state.count * 2),
  quadrupled: computed((state) => state.doubled * 2), // depends on doubled
})

In components

Accessed the same way as regular state:

const { total, isEmpty } = useCart((s) => ({
  total: s.total,
  isEmpty: s.isEmpty,
}))

In actions

Read computed values through state():

class CartActions {
  private cart = state(cart)

  checkout() {
    if (this.cart.isEmpty) return
    processPayment(this.cart.total)
  }
}

computed() vs derive

Both create derived values. Use whichever fits your style:

computed()derive (model option)
DefinitionInline in model initial stateSecond argument to model()
Syntaxcomputed((state) => ...)derive: (state) => ({ key: () => ... })
BehaviorIdenticalIdentical
// computed() style
const cart = model({
  items: [] as CartItem[],
  total: computed((state) => state.items.reduce((s, i) => s + i.price * i.qty, 0)),
})

// derive style
const cart = model(
  { items: [] as CartItem[] },
  { derive: (state) => ({ total: () => state.items.reduce((s, i) => s + i.price * i.qty, 0) }) }
)