repose_core/
lib.rs

1//! # State, Signals, and Effects
2//!
3//! Repose uses a small reactive core instead of an explicit widget tree with
4//! mutable fields. There are three main pieces:
5//!
6//! - `Signal<T>` — observable, reactive value.
7//! - `remember*` — lifecycle‑aware storage bound to composition.
8//! - `effect` / `scoped_effect` — side‑effects with cleanup.
9//!
10//! ## Signals
11//!
12//! `Signal<T>` is a cloneable handle to a piece of state:
13//!
14//! ```rust
15//! use repose_core::*;
16//!
17//! let count = signal(0);
18//! count.set(1);
19//! count.update(|v| *v += 1);
20//! assert_eq!(count.get(), 2);
21//! ```
22//!
23//! Reads participate in a dependency graph: when you call `get()` inside an
24//! observer or `produce_state`, future writes will automatically recompute that
25//! observer.
26//!
27//! ## Remembered state
28//!
29//! UI state is typically held in `remember_*` slots rather than globals:
30//!
31//! ```rust
32//! use repose_core::*;
33//!
34//! fn CounterView() -> View {
35//!     let count = remember_state(|| 0); // Rc<RefCell<i32>>
36//!
37//!     let on_click = {
38//!         let count = count.clone();
39//!         move || *count.borrow_mut() += 1
40//!     };
41//!
42//!     repose_ui::Button(
43//!         format!("Count = {}", *count.borrow()),
44//!         on_click,
45//!     )
46//! }
47//! ```
48//!
49//! - `remember` and `remember_state` are order‑based: the Nth call in a
50//!   composition slot always refers to the Nth stored value.
51//! - `remember_with_key` and `remember_state_with_key` are key‑based and more
52//!   stable across conditional branches.
53//!
54//! ## Derived state
55//!
56//! `produce_state` computes a `Signal<T>` from other signals and recomputes it
57//! automatically when dependencies change:
58//!
59//! ```rust
60//! let first = signal("Jane".to_string());
61//! let last  = signal("Doe".to_string());
62//!
63//! let full = produce_state("full_name", {
64//!     let first = first.clone();
65//!     let last  = last.clone();
66//!     move || format!("{} {}", first.get(), last.get())
67//! });
68//!
69//! assert_eq!(full.get(), "Jane Doe");
70//! ```
71//!
72//! ## Effects and cleanup
73//!
74//! Use `effect` / `scoped_effect` for one‑off side‑effects with cleanups:
75//!
76//! ```rust
77//! use repose_core::*;
78//!
79//! fn Example() -> View {
80//!     scoped_effect(|| {
81//!         log::info!("Mounted Example");
82//!         on_unmount(|| log::info!("Unmounted Example"))
83//!     });
84//!
85//!     // ...
86//!     repose_ui::Box(Modifier::new())
87//! }
88//! ```
89//!
90//! - `effect` runs once when the view is composed and returns a `Dispose`
91//!   guard that will be run when the scope is torn down.
92//! - `scoped_effect` is wired to the current `Scope` and is cleaned up on
93//!   scope disposal (e.g. when a navigation entry is popped).
94//!
95//! For long‑running tasks (network, timers), prefer building small helpers on
96//! top of `scoped_effect` so everything cleans up correctly when the UI that
97//! owns it disappears.
98
99pub mod animation;
100pub mod color;
101pub mod effects;
102pub mod effects_ext;
103pub mod error;
104pub mod geometry;
105pub mod input;
106pub mod locals;
107pub mod modifier;
108pub mod prelude;
109pub mod reactive;
110pub mod render_api;
111pub mod runtime;
112pub mod scope;
113pub mod semantics;
114pub mod signal;
115pub mod state;
116pub mod tests;
117pub mod view;
118
119pub use color::*;
120pub use effects::*;
121pub use effects_ext::*;
122pub use geometry::*;
123pub use locals::*;
124pub use modifier::*;
125pub use prelude::*;
126pub use reactive::*;
127pub use render_api::*;
128pub use runtime::*;
129pub use semantics::*;
130pub use signal::*;
131pub use state::*;
132pub use view::*;
133
134// Ensure a clock is installed even if platform didn't (tests, benches).
135#[doc(hidden)]
136#[allow(dead_code)]
137fn __ensure_clock() {
138    animation::ensure_system_clock();
139}