State, Signals, and Effects
Repose uses a small reactive core instead of an explicit widget tree with mutable fields. There are three main pieces:
Signal<T>- observable, reactive value.remember*- lifecycle‑aware storage bound to composition.effect/scoped_effect- side‑effects with cleanup.
Signals
Signal<T> is a cloneable handle to a piece of state:
use *;
let count = signal;
count.set;
count.update;
assert_eq!;
Reads participate in a dependency graph: when you call get() inside an
observer or produce_state, future writes will automatically recompute that
observer.
Remembered state
UI state is typically held in remember_* slots rather than globals:
use repose_core::*;
fn CounterView() -> View {
let count = remember_mutable(|| 0); // auto-requests a frame on set/update
let on_click = {
let count = count.clone();
move || count.update(|c| *c += 1)
};
repose_ui::Button(
format!("Count = {}", *count.get()),
on_click,
)
}
rememberandremember_mutableare order‑based: the Nth call in a composition slot always refers to the Nth stored value.remember_with_keyandremember_state_with_keyare key‑based and more stable across conditional branches.
Derived state
produce_state computes a Signal<T> from other signals and recomputes it
automatically when dependencies change:
use *;
let first = signal;
let last = signal;
let full = produce_state;
assert_eq!;
Effects and cleanup
Use scoped_effect_once / disposable_effect for mount-once side-effects
with cleanups:
use repose_core::*;
fn Example() -> View {
scoped_effect_once(|| {
log::info!("Mounted Example");
on_unmount(|| log::info!("Unmounted Example"))
});
// ...
repose_ui::Box(Modifier::new())
}
effect/scoped_effectrun on every call and register cleanup on the currentScope. Called directly in a composable body they re-run every frame - useeffect_once/scoped_effect_oncefor mount-once setup.disposable_effect(key, ..)re-runs on key change and cleans up on unmount (callsite-keyed, branch-stable).launched_effect!(key, ..)is the cancellable launched variant;launched_effect_uncancelled!is explicit fire-and-forget.
For long‑running tasks (network, timers), prefer building small helpers on
top of disposable_effect so everything cleans up correctly when the UI that
owns it disappears.