Signal
Creation, tracked reads, equality-checked writes, and mutation rules.
Signal<T> (incular::prelude, from incular-runtime) is the application
reactive primitive: single-threaded shared state. Reading inside a build
subscribes that element; reading outside returns the value with no dependency.
Clones share identity across windows on the same UI thread.
| Method | Effect |
|---|---|
new(value) | Create with initial value (no Default) |
get() | Tracked read, clones value |
get_untracked() | Read without subscribing |
set(value) | Equality-checked write, returns bool |
update(f) | In-place mutation, always notifies |
update_if_changed(f) | Mutate, returns whether it changed |
dependent_count() | Current subscriber count |
Creating
Signal::new(value) takes an explicit initial value (no Default) — see the
runnable program below.
Create once — outside the build or via cx.state — then clone into builders
and callbacks. Never create a fresh signal inside a build on every run.
Reading
get() tracks the dependency and clones; get_untracked() reads without
subscribing (callbacks, logging); dependent_count() reports subscribers.
get() inside a builder subscribes the current element (or the current
Memo/Effect).
Outside a build it just returns the value.
Writing
set (equality-checked, returns bool), update (unconditional notify),
update_if_changed — exercised via update in the runnable program above.
Rules:
- Write only from event callbacks,
Effects, orActions — callingsetwhile a widget builder runs panics. - Prefer
setfor replacements (skips redundant rebuilds),updatefor in-place mutation.
Devtools variants
Devtools variants
With the devtools feature: signal.devtools("counter") (named, T: Debug)
and signal.devtools_editable() (editable for bool/i64/u64/f64/String).
These require the feature flag and are intentionally not in the runnable
program, which builds on default features.
Canonical pattern
use incular::prelude::*;
fn main() {
let count = Signal::new(0u32);
let app = Application::new(move |_cx| {
let tapped = count.clone();
let shown = count.clone();
Container::builder()
.padding(EdgeInsets::all(24.0))
.alignment(Alignment::CENTER)
.child(
GestureDetector::new(Text::new(format!("Count: {}", shown.get())))
.on_tap(move || {
tapped.update(|c| *c += 1);
}),
)
.build()
.into()
})
.expect("valid application");
incular::run(app).expect("run application");
}Related: Memo, Effect,
Action, Tasks,
Restoration.