From 8c4576396b2f34e4d0d0def237c18c46f7b98d9d Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sun, 1 Feb 2026 13:39:37 +0100 Subject: [PATCH] Improved state structure --- src/state/effect.rs | 55 +++++++++++++++ src/state/mod.rs | 44 ++++++++++++ src/state/mount.rs | 58 ++++++++++++++++ src/{ => state}/state.rs | 142 +++------------------------------------ 4 files changed, 168 insertions(+), 131 deletions(-) create mode 100644 src/state/effect.rs create mode 100644 src/state/mod.rs create mode 100644 src/state/mount.rs rename src/{ => state}/state.rs (52%) diff --git a/src/state/effect.rs b/src/state/effect.rs new file mode 100644 index 0000000..fc6d2e5 --- /dev/null +++ b/src/state/effect.rs @@ -0,0 +1,55 @@ +//! # Effect hooks module +//! +//! Provides React-like use_effect hooks for managing effects. +//! This module includes use_effect and use_sync_effect. + +use std::sync::{Arc, Mutex}; + +use super::{state::State, HookDependency, HookEffect}; +use crate::component::context::Context; + +/// Executes a function when dependencies change +/// +/// Similar to React's useEffect. The provided function is executed +/// when any of the dependencies change. +pub fn use_effect(f: F, dependencies: &[&dyn HookDependency]) { + let f = Arc::new(Mutex::new(f)); + let hook = HookEffect(Arc::new(Mutex::new({ + let f = f.clone(); + move || { + let f = f.clone(); + std::thread::spawn(move || (f.lock().unwrap())()); + } + }))); + + for d in dependencies { + d.on_update(hook.clone()); + } +} + +/// Synchronizes state changes back to the context as events +/// +/// Creates an effect that emits an event whenever the state changes. +/// The encoder function converts state values to events. +pub fn use_sync_effect< + T: Send + Sync + 'static, + Ev: Send + Sync + 'static, + E: Fn(&State) -> Ev + Send + Sync + 'static, +>( + cx: &Arc, + state: &State, + encoder: E, + deps: &[&dyn HookDependency], +) { + use_effect( + { + let state = state.clone(); + let cx = cx.clone(); + move || { + let ev = encoder(&state); + cx.emit_event(ev); + } + }, + deps, + ); +} diff --git a/src/state/mod.rs b/src/state/mod.rs new file mode 100644 index 0000000..0dc2c0e --- /dev/null +++ b/src/state/mod.rs @@ -0,0 +1,44 @@ +//! # State Management Module +//! +//! Provides React-like hooks for managing component state and side effects. +//! This module includes useState, useEffect, useMount, and state synchronization hooks. + +use std::{ + fmt::{Debug, Formatter, Result as FmtResult}, + sync::{Arc, Mutex}, +}; + +mod effect; +mod mount; +mod state; +pub use effect::*; +pub use mount::*; +pub use state::*; + +/// Effect callback that can be triggered by state changes +#[derive(Clone)] +pub struct HookEffect(Arc>); + +/// Trait for values that can be tracked as dependencies in hooks +pub trait HookDependency: Send + Sync { + /// Register an effect to be triggered on updates + fn on_update(&self, hook: HookEffect); +} + +impl HookEffect { + /// Creates a new effect from a function + pub fn new(f: F) -> Self { + Self(Arc::new(Mutex::new(f))) + } + + /// Executes the effect function + pub fn call(&self) { + (self.0.lock().unwrap())() + } +} + +impl Debug for HookEffect { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "HookEffect") + } +} diff --git a/src/state/mount.rs b/src/state/mount.rs new file mode 100644 index 0000000..468a4ec --- /dev/null +++ b/src/state/mount.rs @@ -0,0 +1,58 @@ +//! # Mount hooks module +//! +//! Provides React-like Mount hooks for managing effects. +//! This module includes Mount, use_mount and use_mount_manual. + +use std::{ + fmt::Debug, + sync::{Arc, Mutex}, +}; + +use super::{HookDependency, HookEffect}; + +/// Mount lifecycle hook +/// +/// Tracks whether a component has been mounted and executes +/// any pending mount effects. +#[derive(Debug, Clone)] +pub struct Mount(Arc>, Arc>>); + +/// Creates a mount lifecycle hook +/// +/// Returns a Mount that tracks component lifecycle and executes +/// effects after mounting. +pub fn use_mount() -> Mount { + Mount(Arc::new(Mutex::new(true)), Arc::new(Mutex::new(Vec::new()))) +} + +/// Creates a manual mount lifecycle hook +/// +/// Similar to use_mount but the component starts as unmounted. +/// Must call .mount() to trigger mounted effects. +pub fn use_mount_manual() -> Mount { + Mount( + Arc::new(Mutex::new(false)), + Arc::new(Mutex::new(Vec::new())), + ) +} + +impl HookDependency for Mount { + fn on_update(&self, hook: HookEffect) { + if *self.0.lock().unwrap() { + hook.call(); + } else { + self.1.lock().unwrap().push(hook); + } + } +} + +impl Mount { + /// Mark the component as mounted and execute pending effects + pub fn mount(&self) { + *self.0.lock().unwrap() = true; + for hook_effect in self.1.lock().unwrap().iter() { + hook_effect.call(); + } + self.1.lock().unwrap().clear(); + } +} diff --git a/src/state.rs b/src/state/state.rs similarity index 52% rename from src/state.rs rename to src/state/state.rs index 8e4c7a7..d909cb8 100644 --- a/src/state.rs +++ b/src/state/state.rs @@ -1,7 +1,7 @@ -//! # State Management Module +//! # State hooks module //! -//! Provides React-like hooks for managing component state and side effects. -//! This module includes useState, useEffect, useMount, and state synchronization hooks. +//! Provides React-like use_state hooks for managing states. +//! This module includes use_state and use_sync_state. use std::{ any::Any, @@ -10,12 +10,9 @@ use std::{ sync::{Arc, Mutex, MutexGuard}, }; +use super::{HookDependency, HookEffect}; use crate::component::context::Context; -/// Effect callback that can be triggered by state changes -#[derive(Clone)] -pub struct HookEffect(Arc>); - /// State holder for reactive values /// /// Similar to React's useState hook. Holds a value and tracks dependents @@ -38,23 +35,6 @@ pub struct Inner<'a, T> { updated: bool, } -/// Mount lifecycle hook -/// -/// Tracks whether a component has been mounted and executes -/// any pending mount effects. -#[derive(Debug, Clone)] -pub struct Mount(Arc>, Arc>>); - -/// Creates a new state value -/// -/// Returns a State that can be read and written from multiple threads. -pub fn use_state(v: T) -> State { - State { - value: Arc::new(Mutex::new(v)), - dependents: Arc::new(Mutex::new(Vec::new())), - } -} - impl State { /// Gets a cloned copy of the state value /// @@ -106,24 +86,6 @@ impl Display for State { } } -impl Debug for HookEffect { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - write!(f, "HookEffect") - } -} - -impl HookEffect { - /// Creates a new effect from a function - pub fn new(f: F) -> Self { - Self(Arc::new(Mutex::new(f))) - } - - /// Executes the effect function - pub fn call(&self) { - (self.0.lock().unwrap())() - } -} - /// Inner implements Deref for read access and DerefMut for write access /// On drop after mutation, automatically triggers dependent effects impl Drop for Inner<'_, T> { @@ -150,77 +112,12 @@ impl DerefMut for Inner<'_, T> { } } -/// Trait for values that can be tracked as dependencies in hooks -pub trait HookDependency: Send + Sync { - /// Register an effect to be triggered on updates - fn on_update(&self, hook: HookEffect); -} - impl HookDependency for State { fn on_update(&self, hook: HookEffect) { self.dependents.lock().unwrap().push(hook); } } -impl HookDependency for Mount { - fn on_update(&self, hook: HookEffect) { - if *self.0.lock().unwrap() { - hook.call(); - } else { - self.1.lock().unwrap().push(hook); - } - } -} - -impl Mount { - /// Mark the component as mounted and execute pending effects - pub fn mount(&self) { - *self.0.lock().unwrap() = true; - for hook_effect in self.1.lock().unwrap().iter() { - hook_effect.call(); - } - self.1.lock().unwrap().clear(); - } -} - -/// Executes a function when dependencies change -/// -/// Similar to React's useEffect. The provided function is executed -/// when any of the dependencies change. -pub fn use_effect(f: F, dependencies: &[&dyn HookDependency]) { - let f = Arc::new(Mutex::new(f)); - let hook = HookEffect(Arc::new(Mutex::new({ - let f = f.clone(); - move || { - let f = f.clone(); - std::thread::spawn(move || (f.lock().unwrap())()); - } - }))); - - for d in dependencies { - d.on_update(hook.clone()); - } -} - -/// Creates a mount lifecycle hook -/// -/// Returns a Mount that tracks component lifecycle and executes -/// effects after mounting. -pub fn use_mount() -> Mount { - Mount(Arc::new(Mutex::new(true)), Arc::new(Mutex::new(Vec::new()))) -} - -/// Creates a manual mount lifecycle hook -/// -/// Similar to use_mount but the component starts as unmounted. -/// Must call .mount() to trigger mounted effects. -pub fn use_mount_manual() -> Mount { - Mount( - Arc::new(Mutex::new(false)), - Arc::new(Mutex::new(Vec::new())), - ) -} - /// Synchronizes state with events from the context /// /// Creates state that is automatically updated when events are emitted @@ -244,29 +141,12 @@ pub fn use_sync_state< state } -/// Synchronizes state changes back to the context as events +/// Creates a new state value /// -/// Creates an effect that emits an event whenever the state changes. -/// The encoder function converts state values to events. -pub fn use_sync_effect< - T: Send + Sync + 'static, - Ev: Send + Sync + 'static, - E: Fn(&State) -> Ev + Send + Sync + 'static, ->( - cx: &Arc, - state: &State, - encoder: E, - deps: &[&dyn HookDependency], -) { - use_effect( - { - let state = state.clone(); - let cx = cx.clone(); - move || { - let ev = encoder(&state); - cx.emit_event(ev); - } - }, - deps, - ); +/// Returns a State that can be read and written from multiple threads. +pub fn use_state(v: T) -> State { + State { + value: Arc::new(Mutex::new(v)), + dependents: Arc::new(Mutex::new(Vec::new())), + } }