# State Management and Reactivity OSUI provides a built-in, lightweight state management system that enables reactive updates to your UI. This system is centered around the `State` struct and the `DependencyHandler` trait, allowing `DynWidget`s to automatically re-render when their associated data changes. ## `State`: Your Reactive Data Container The `State` struct is a wrapper around your data `T` that facilitates dependency tracking. ```rust #[derive(Debug, Clone)] pub struct State { inner: Arc>>, } #[derive(Debug)] pub struct Inner { value: T, dependencies: usize, // Number of widgets depending on this state changed: usize, // Counter for changes waiting to be processed by dependents } ``` * **`Arc>>`**: The core of `State` is its use of `Arc` and `Mutex`. * `Arc` allows `State` instances to be shared across multiple widgets and threads without needing to clone the underlying data `T` itself, which is crucial for `DynWidget`s that track multiple dependencies. * `Mutex` ensures safe concurrent access to the `value` and metadata (`dependencies`, `changed`), preventing data races. ### Creating State (`use_state`) You create a new `State` instance using the `use_state` helper function: ```rust pub fn use_state(v: T) -> State { /* ... */ } ``` **Example:** ```rust use osui::prelude::*; fn main() -> std::io::Result<()> { let screen = Screen::new(); screen.extension(InputExtension); screen.extension(RelativeFocusExtension::new()); // Create a new state variable for a counter let count = use_state(0); // Spawn a thread to increment the counter every second std::thread::spawn({ let count = count.clone(); // Clone the Arc> for the new thread move || loop { // Get a mutable lock on the Inner to modify the value // DerefMut implementation on Inner automatically marks it as changed *count.get() += 1; std::thread::sleep(std::time::Duration::from_secs(1)); } }); rsx! { // Declare the widget as dependent on `count` %count Div { // Access the value using Deref on Inner format!("This number increments: {}", count.get()) } }.draw(&screen); screen.run()?; Ok(()) } ``` ### Accessing and Modifying State * **`State::get()`**: Returns a `MutexGuard<'_, Inner>`. This provides mutable access to the underlying `value` within `Inner`. * `Inner` implements `Deref` and `DerefMut` for `T`. This means you can treat `count.get()` like a direct reference to `T`. * Crucially, when you use `DerefMut` (e.g., `*count.get() += 1`), the `changed` counter within `Inner` is automatically incremented. This is how OSUI knows the state has been modified and needs to trigger a re-render. * **`State::get_dl()`**: (Short for "get, don't lock") Returns a cloned copy of the value. This is useful when you only need to read the value and want to avoid holding the `MutexGuard` for longer than necessary, which can prevent deadlocks in complex scenarios. However, it doesn't mark the state as changed. * **`State::set(v: T)`**: Replaces the entire value and marks the state as changed. * **`State::update()`**: Explicitly marks the state as changed *without* modifying its value. Useful if internal parts of `T` are modified outside of direct `DerefMut` access. ## `DependencyHandler` Trait The `DependencyHandler` trait is the interface through which `DynWidget`s observe changes in their dependencies. `State` implements this trait. ```rust pub trait DependencyHandler: std::fmt::Debug + Send + Sync { fn add(&self); fn check(&self) -> bool; } ``` * **`add()`**: Called when a `DynWidget` registers itself as a dependent of this `State`. It increments the `dependencies` counter within `Inner`. * **`check()`**: Called by `DynWidget`s (specifically by `DynWidget::auto_refresh()`) to determine if the state has changed since the last check. * It decrements the `changed` counter if it's greater than zero, signifying that a change has been "consumed" by a dependent. * It returns `true` if `changed` was greater than zero, indicating a fresh update. ### How Reactivity Works 1. **Widget Creation**: When `rsx!` creates a `DynWidget` with a `%state_var` dependency, `state_var.add()` is called, incrementing `state_var.inner.dependencies`. 2. **State Modification**: When `*state_var.get() = new_value` or `state_var.set(new_value)` is called, the `state_var.inner.changed` counter is set to `state_var.inner.dependencies`. This means *all* widgets currently depending on this state are marked for a refresh. 3. **Automatic Refresh**: In each rendering frame, `DynWidget::auto_refresh()` is called. * It iterates through its registered `DependencyHandler`s. * For each dependency, it calls `dependency.check()`. * If `check()` returns `true` (meaning the state has changed and hasn't been consumed yet by this widget), the `DynWidget`'s internal `load` closure is re-executed (`self.refresh()`). This rebuilds the widget's `Element` and `Component`s, picking up the new state value. * The `check()` method decrements the `changed` counter, ensuring that a single modification to the state triggers exactly one rebuild for each dependent widget. 4. **Re-render**: The rebuilt widget is then rendered on the next frame, reflecting the updated state. This system provides a robust and efficient way to manage dynamic UI elements, abstracting away the complexities of manual DOM updates and allowing developers to focus on defining their UI's desired state.