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:
from reaktiv import computed, signal
price = signal(10)
quantity = signal(2)
@computed
def total():
return price() * quantity()
print(total()) # 20
Use typed decorator syntax when you want to state the result type explicitly:
from reaktiv import computed, signal
name = signal("Ada")
@computed[str]
def normalized_name():
return name().strip().lower()
print(normalized_name()) # ada
Custom equality can suppress downstream updates when two computed values should be treated as equivalent:
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())
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.