Skip to content

Computed Signal API

Computed and computed create computed signals: reactive values that derive from other signals, recompute lazily, and cache their latest value until a dependency changes.

Computed is kept as the uppercase constructor-style API. computed is the preferred lowercase decorator/factory spelling for new code, especially inside ReactiveModel classes.

Computed / computed Factory

Create a computed signal from a callable:

Editor (session: default) Run
from reaktiv import Computed, Signal

price = Signal(10)
quantity = Signal(2)

total = Computed(lambda: price() * quantity())

print(total())  # 20
Output Clear

Use lowercase decorator syntax for new code:

Editor (session: default) Run
from reaktiv import Signal, computed

price = Signal(10)
quantity = Signal(2)

@computed
def total() -> int:
    return price() * quantity()

print(total())  # 20
Output Clear

When omitting a return annotation, use typed decorator syntax so type checkers can preserve the returned signal type:

Editor (session: default) Run
from reaktiv import Signal, computed

name = Signal("Ada")

@computed[str]
def normalized_name():
    return name().strip().lower()

print(normalized_name())  # ada
Output Clear

Custom equality can suppress downstream updates when two computed values should be treated as equivalent:

Editor (session: default) Run
from reaktiv import Signal, computed

temperature = Signal(21.04)

@computed[float](equal=lambda left, right: round(left, 1) == round(right, 1))
def rounded_temperature():
    return temperature()

print(rounded_temperature())
temperature.set(21.05)
print(rounded_temperature())
Output Clear

reaktiv.ComputeSignal

A computed signal that derives its value from other signals.

ComputeSignal automatically tracks dependencies on other signals and recomputes its value when any dependency changes. Computations are lazy and cached - they only run when accessed and dependencies have changed.

Parameters:

Name Type Description Default
compute_fn Callable[[], T]

A function that computes the signal's value from other signals

required
equal Optional[Callable[[T, T], bool]]

Optional custom equality function for change detection

None

Examples:

Basic computed signal:

from reaktiv import Signal, Computed

first_name = Signal("John")
last_name = Signal("Doe")

# Create computed signal
full_name = Computed(lambda: f"{first_name()} {last_name()}")

print(full_name())  # "John Doe"

first_name.set("Jane")
print(full_name())  # "Jane Doe"

Lazy computation:

from reaktiv import Signal, Computed

x = Signal(10)
y = Signal(20)

def expensive_computation():
    print("Computing...")
    return x() * y()

result = Computed(expensive_computation)

# Nothing happens yet - computation is lazy

# First access - computation runs
print(result())  # Prints: "Computing..." then "200"

# Second access - no computation (cached)
print(result())  # Just prints "200"

# Change a dependency
x.set(5)

# Next access will recompute
print(result())  # Prints: "Computing..." then "100"

Decorator pattern:

from reaktiv import Signal, Computed

price = Signal(100)
quantity = Signal(2)

@Computed
def total():
    return price() * quantity()

print(total())  # 200

Error handling:

from reaktiv import Signal, Computed

x = Signal(10)

# Computed signal with potential error
result = Computed(lambda: 100 / x())

print(result())  # 10.0 (100 / 10)

# Set x to 0, causing division by zero
x.set(0)

# Exception is propagated to caller
try:
    print(result())
except ZeroDivisionError as e:
    print(f"Error: {e}")

# After fixing, computation works again
x.set(5)
print(result())  # 20.0 (100 / 5)

Note

Computed signals are lazy - they only compute when accessed and cache the result until dependencies change. When a computation raises an exception, it is propagated to the caller for flexible error handling.