linked API¶
Use linked(...) or @linked for writable derived state: a value can be
overridden manually, then reset when its source context changes.
from reaktiv import linked, signal
page = signal(1)
@linked
def selection():
return f"default-for-page-{page()}"
selection.set("custom-choice")
print(selection()) # custom-choice
page.set(2)
print(selection()) # default-for-page-2
Naming
Prefer linked(...) and @linked in application code. LinkedSignal is
the concrete writable-derived signal class. Linked remains available as a
compatibility alias.
reaktiv.linked
¶
LinkedSignal.
LinkedDescriptor
¶
Bases: PerInstanceDescriptor['LinkedSignal[T]'], Generic[T]
Descriptor that creates one LinkedSignal per owner instance.
LinkedSignal
¶
Bases: ComputeSignal[T], Generic[T]
A writable signal that automatically recomputes when source signals change.
LinkedSignal combines the benefits of computed signals (automatic updates) with writable signals (can be set manually). When source signals change, it recomputes. When manually set, it holds that value until sources change again.
Perfect for form inputs, UI state, and derived values that users can override.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
computation_or_source
|
Union[Callable[..., T], Signal[U], None]
|
The computation function (simple pattern) or source signal |
None
|
source
|
Optional[Union[Signal[U], Callable[[], U]]]
|
Optional source signal or callable (advanced pattern) |
None
|
computation
|
Optional[Callable[[U, Optional[PreviousState[T]]], T]]
|
Optional computation function (advanced pattern) |
None
|
equal
|
Optional[Callable[[T, T], bool]]
|
Optional custom equality function for change detection |
None
|
Examples:
Simple pattern (automatic derivation):
from reaktiv import linked, signal
fahrenheit = signal(32)
# Celsius automatically computes from Fahrenheit
@linked
def celsius():
return (fahrenheit() - 32) * 5 / 9
print(celsius()) # 0.0
fahrenheit.set(212)
print(celsius()) # 100.0
# Can override manually
celsius.set(25)
print(celsius()) # 25.0 (manual value)
# Changing source recomputes
fahrenheit.set(68)
print(celsius()) # 20.0 (recomputed from source)
Form input pattern:
from reaktiv import effect, linked, signal
# Server data
server_name = signal("John Doe")
# Form input linked to server data
@linked
def input_value():
return server_name()
# Track changes (keep reference to prevent GC)
def print_input():
print(f"Input: {input_value()}")
input_effect = effect(print_input)
# Prints: "Input: John Doe"
# User edits the input
input_value.set("Jane Smith")
# Prints: "Input: Jane Smith"
# Server data refreshes
server_name.set("John Updated")
# Prints: "Input: John Updated" (reset to server value)
Advanced pattern (with previous state):
from reaktiv import PreviousState, linked, signal
total_pages = signal(10)
def clamp_page(page_num, prev):
max_page = total_pages()
if max_page == 0:
return None
# First time or source changed - use default
if prev is None or prev.source != max_page:
return 1
# Clamp to valid range
return max(1, min(prev.value, max_page))
current_page = linked(
source=total_pages,
computation=clamp_page
)
print(current_page()) # 1 (default)
current_page.set(5)
print(current_page()) # 5
total_pages.set(3)
print(current_page()) # 3 (clamped to max)
As decorator:
from reaktiv import linked, signal
source = signal(0)
@linked
def derived():
return source() * 2
print(derived()) # 0
source.set(5)
print(derived()) # 10
derived.set(100)
print(derived()) # 100 (manual override)
reaktiv.LinkedSignal
¶
A writable signal that automatically recomputes when source signals change.
LinkedSignal combines the benefits of computed signals (automatic updates) with writable signals (can be set manually). When source signals change, it recomputes. When manually set, it holds that value until sources change again.
Perfect for form inputs, UI state, and derived values that users can override.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
computation_or_source
|
Union[Callable[..., T], Signal[U], None]
|
The computation function (simple pattern) or source signal |
None
|
source
|
Optional[Union[Signal[U], Callable[[], U]]]
|
Optional source signal or callable (advanced pattern) |
None
|
computation
|
Optional[Callable[[U, Optional[PreviousState[T]]], T]]
|
Optional computation function (advanced pattern) |
None
|
equal
|
Optional[Callable[[T, T], bool]]
|
Optional custom equality function for change detection |
None
|
Examples:
Simple pattern (automatic derivation):
from reaktiv import linked, signal
fahrenheit = signal(32)
# Celsius automatically computes from Fahrenheit
@linked
def celsius():
return (fahrenheit() - 32) * 5 / 9
print(celsius()) # 0.0
fahrenheit.set(212)
print(celsius()) # 100.0
# Can override manually
celsius.set(25)
print(celsius()) # 25.0 (manual value)
# Changing source recomputes
fahrenheit.set(68)
print(celsius()) # 20.0 (recomputed from source)
Form input pattern:
from reaktiv import effect, linked, signal
# Server data
server_name = signal("John Doe")
# Form input linked to server data
@linked
def input_value():
return server_name()
# Track changes (keep reference to prevent GC)
def print_input():
print(f"Input: {input_value()}")
input_effect = effect(print_input)
# Prints: "Input: John Doe"
# User edits the input
input_value.set("Jane Smith")
# Prints: "Input: Jane Smith"
# Server data refreshes
server_name.set("John Updated")
# Prints: "Input: John Updated" (reset to server value)
Advanced pattern (with previous state):
from reaktiv import PreviousState, linked, signal
total_pages = signal(10)
def clamp_page(page_num, prev):
max_page = total_pages()
if max_page == 0:
return None
# First time or source changed - use default
if prev is None or prev.source != max_page:
return 1
# Clamp to valid range
return max(1, min(prev.value, max_page))
current_page = linked(
source=total_pages,
computation=clamp_page
)
print(current_page()) # 1 (default)
current_page.set(5)
print(current_page()) # 5
total_pages.set(3)
print(current_page()) # 3 (clamped to max)
As decorator:
from reaktiv import linked, signal
source = signal(0)
@linked
def derived():
return source() * 2
print(derived()) # 0
source.set(5)
print(derived()) # 10
derived.set(100)
print(derived()) # 100 (manual override)