Documentation / Fundamentals
Environment & effects
Share models across your view tree and tie external work to mounted lifetimes.
Provide values down the tree
Typed environment keys define a Clone + PartialEq value and a default. Provide a local override with .environment::<Key>(value) and read it with cx.environment::<Key>(). The read becomes a reactive dependency.
For shared models, environment_object and cx.object() use pointer identity. Store observable model fields in graph state. Intermediate layout components do not need to forward the model as props.
// The clock root provides a shared model.
clock_face().environment_object(model)
// A descendant reads a binding on that model.
// cx.read(&model.reading) subscribes to changes.Own work with an effect
An effect starts after a successful commit and returns a cleanup closure. Changed dependencies and committed unmounts clean up the previous effect. Discarded candidates never start effects.
Inside a #[view] function, #[effect] declares this lifecycle. In the clock example, the injected subscribe function owns the platform timer and returns its cleanup.
#[effect(id = "one-second-clock", captures(subscribe, reading, scope))]
let clock_timer = |_| {
subscribe(reading.clone(), scope.clone())
};This is an excerpt from the clock pattern, not a standalone function. Effects must be unconditional top-level declarations, with unique IDs within the view.
Make dependencies explicit
Effect dependencies default to (), so setup runs once per mounted identity. Add deps = period or deps = (period, enabled) to restart when those values change according to PartialEq.
Reads inside setup do not establish view dependencies. Read reactive values during view evaluation and include inputs that should restart setup explicitly. captures(...) clones handles for the closure; it does not infer dependencies.
Scoped update handles discard queued completions after the owning view unmounts. Use them for asynchronous work that might complete after navigation or removal.