Skip to content

computed API

Use computed(...) or @computed to create reactive derived values. Computed signals derive from other signals, recompute lazily, and cache their latest value until a dependency changes.

Naming

Prefer computed(...) and @computed in application code. Computed remains supported for existing uppercase constructor/decorator-style code. ComputeSignal is the concrete readable signal type returned by the factory.

Create Derived State

Create a computed signal from a callable:

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

price = signal(10)
quantity = signal(2)

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

print(total())  # 20
Output Clear

Use typed decorator syntax when you want to state the result type explicitly:

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

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 computed, signal

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 computed, signal

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

@computed
def full_name():
    return f"{first_name()} {last_name()}"

print(full_name())  # "John Doe"

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

Lazy computation:

from reaktiv import computed, signal

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 computed, signal

price = signal(100)
quantity = signal(2)

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

print(total())  # 200

Error handling:

from reaktiv import computed, signal

x = signal(10)

# Computed signal with potential error
@computed
def result():
    return 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.