freya_core/lifecycle/
state.rs

1use std::{
2    cell::RefCell,
3    fmt::{
4        Debug,
5        Display,
6    },
7    mem::MaybeUninit,
8    ops::Deref,
9    rc::Rc,
10};
11
12use generational_box::{
13    AnyStorage,
14    GenerationalBox,
15    UnsyncStorage,
16};
17use rustc_hash::FxHashSet;
18
19use crate::{
20    current_context::CurrentContext,
21    prelude::use_hook,
22    reactive_context::ReactiveContext,
23    scope_id::ScopeId,
24};
25
26/// A reactive state container that holds a value of type `T` and manages subscriptions to changes.
27///
28/// `State<T>` is the fundamental reactive primitive in Freya. It allows you to store mutable state
29/// that automatically triggers re-renders in components that read from it when the value changes.
30///
31/// # Key Features
32///
33/// - **Reactive**: Components automatically re-render when the state value changes.
34/// - **Copy**: `State<T>` implements `Copy`, making it cheap to pass around.
35/// - **Shared**: Multiple components can read from and write to the same state.
36/// - **Scoped**: State is automatically cleaned up when its owning component unmounts.
37///
38/// # Basic Usage
39///
40/// ```rust,no_run
41/// use freya::prelude::*;
42///
43/// fn counter() -> impl IntoElement {
44///     // Create reactive state
45///     let mut count = use_state(|| 0);
46///
47///     rect().child(format!("Count: {}", count.read())).child(
48///         Button::new()
49///             .child("Increment")
50///             .on_press(move |_| *count.write() += 1),
51///     )
52/// }
53/// ```
54///
55/// # Reading State
56///
57/// - `state.read()` - Reads the current value and subscribes the current component to changes.
58/// - `state.peek()` - Reads the current value without subscribing (rarely needed).
59///
60/// # Writing State
61///
62/// - `state.write()` - Gets a mutable reference to modify the value.
63/// - `state.set(new_value)` - Replaces the current value.
64/// - `state.with_mut(|mut_ref| { /* modify */ })` - Modifies using a closure.
65///
66/// # Advanced Patterns
67///
68/// ## Conditional Updates
69///
70/// ```rust,no_run
71/// # use freya::prelude::*;
72/// let mut count = use_state(|| 0);
73///
74/// // Only update if the new value is different
75/// count.set_if_modified(5);
76///
77/// // Update and run additional logic
78/// count.set_if_modified_and_then(10, || {
79///     println!("Count reached 10!");
80/// });
81/// ```
82///
83/// ## Working with Options
84///
85/// ```rust,no_run
86/// # use freya::prelude::*;
87/// let mut optional_value = use_state(|| Some(42));
88///
89/// // Take ownership of the contained value
90/// let taken_value = optional_value.take(); // Returns Option<i32>
91/// ```
92///
93/// ## Copy Types
94///
95/// For `Copy` types, you can call the state as a function to read:
96///
97/// ```rust,no_run
98/// # use freya::prelude::*;
99/// let count = use_state(|| 0);
100///
101/// // These are equivalent:
102/// let value1 = count.read().clone();
103/// let value2 = count(); // Only works for Copy types
104/// ```
105///
106/// # Global State
107///
108/// For state that persists across the entire application lifecycle:
109///
110/// ```rust,no_run
111/// # use freya::prelude::*;
112/// // Create global state (use sparingly)
113/// let global_count = State::create_global(0);
114/// ```
115///
116/// # Thread Safety
117///
118/// `State<T>` is not thread-safe and should only be used within the main UI thread.
119/// For cross-thread communication, consider using channels or other synchronization primitives.
120///
121/// # Performance Notes
122///
123/// - Reading state subscribes the current component, causing re-renders when it changes.
124/// - Use `peek()` only when you specifically don't want reactivity.
125/// - Prefer `set_if_modified()` over `set()` when the value might not have changed.
126pub struct State<T> {
127    key: GenerationalBox<T>,
128    subscribers: GenerationalBox<Rc<RefCell<FxHashSet<ReactiveContext>>>>,
129}
130
131impl<T: 'static> PartialEq for State<T> {
132    fn eq(&self, other: &Self) -> bool {
133        self.key.ptr_eq(&other.key)
134    }
135}
136
137impl<T: 'static> Eq for State<T> {}
138
139/// Allow calling the states as functions.
140/// Limited to `Copy` values only.
141impl<T: Copy + 'static> Deref for State<T> {
142    type Target = dyn Fn() -> T;
143
144    fn deref(&self) -> &Self::Target {
145        unsafe { State::deref_impl(self) }
146    }
147}
148
149impl<T> State<T> {
150    /// Adapted from https://github.com/DioxusLabs/dioxus/blob/a4aef33369894cd6872283d6d7d265303ae63913/packages/signals/src/read.rs#L246
151    /// SAFETY: You must call this function directly with `self` as the argument.
152    /// This function relies on the size of the object you return from the deref
153    /// being the same as the object you pass in
154    #[doc(hidden)]
155    unsafe fn deref_impl<'a>(state: &State<T>) -> &'a dyn Fn() -> T
156    where
157        Self: Sized + 'a,
158        T: Clone + 'static,
159    {
160        // https://github.com/dtolnay/case-studies/tree/master/callable-types
161
162        // First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit<Self>).
163        let uninit_callable = MaybeUninit::<Self>::uninit();
164        // Then move that value into the closure. We assume that the closure now has a in memory layout of Self.
165        let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }).clone();
166
167        // Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure.
168        let size_of_closure = std::mem::size_of_val(&uninit_closure);
169        assert_eq!(size_of_closure, std::mem::size_of::<Self>());
170
171        // Then cast the lifetime of the closure to the lifetime of &self.
172        fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T {
173            b
174        }
175        let reference_to_closure = cast_lifetime(
176            {
177                // The real closure that we will never use.
178                &uninit_closure
179            },
180            #[allow(clippy::missing_transmute_annotations)]
181            // We transmute self into a reference to the closure. This is safe because we know that the closure has the same memory layout as Self so &Closure == &Self.
182            unsafe {
183                std::mem::transmute(state)
184            },
185        );
186
187        // Cast the closure to a trait object.
188        reference_to_closure as &_
189    }
190}
191
192impl<T: std::ops::Not<Output = T> + Clone + 'static> State<T> {
193    /// Toggle the boolean-like value and return the new value.
194    ///
195    /// This method negates the current value using the `!` operator and returns
196    /// the new value after updating the state.
197    ///
198    /// # Requirements
199    ///
200    /// The type `T` must implement `std::ops::Not<Output = T> + Clone`.
201    ///
202    /// # Example
203    ///
204    /// ```rust,no_run
205    /// # use freya::prelude::*;
206    /// let mut flag = use_state(|| false);
207    ///
208    /// // Toggle and get the new value
209    /// let new_value = flag.toggled(); // false -> true, returns true
210    /// assert_eq!(new_value, true);
211    /// ```
212    ///
213    /// # Common Types
214    ///
215    /// Works with `bool`, custom enum types, etc.
216    pub fn toggled(&mut self) -> T {
217        let value = self.read().clone();
218        let neg_value = !value;
219        self.set(neg_value.clone());
220        neg_value
221    }
222
223    /// Toggle the boolean-like value without returning it.
224    ///
225    /// This is a convenience method that toggles the value but discards the result.
226    /// Equivalent to calling [toggled](Self::toggled) and ignoring the return value.
227    ///
228    /// # Example
229    ///
230    /// ```rust,no_run
231    /// # use freya::prelude::*;
232    /// let mut is_visible = use_state(|| false);
233    ///
234    /// // Toggle visibility
235    /// is_visible.toggle(); // false -> true
236    /// ```
237    pub fn toggle(&mut self) {
238        self.toggled();
239    }
240}
241
242pub enum ReadableRef<T: 'static> {
243    Ref(ReadRef<'static, T>),
244    Borrowed(Rc<T>),
245}
246
247impl<T: 'static + Debug> Debug for ReadableRef<T> {
248    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249        match self {
250            Self::Ref(r) => r.fmt(f),
251            Self::Borrowed(r) => r.deref().fmt(f),
252        }
253    }
254}
255
256impl<T: 'static + Display> Display for ReadableRef<T> {
257    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
258        match self {
259            Self::Ref(r) => r.fmt(f),
260            Self::Borrowed(r) => r.deref().fmt(f),
261        }
262    }
263}
264
265impl<T> Deref for ReadableRef<T> {
266    type Target = T;
267    fn deref(&self) -> &Self::Target {
268        match self {
269            Self::Ref(r) => r.deref(),
270            Self::Borrowed(b) => b,
271        }
272    }
273}
274
275pub type ReadRef<'a, T> =
276    <generational_box::UnsyncStorage as generational_box::AnyStorage>::Ref<'a, T>;
277
278pub type WriteRef<'a, T> =
279    <generational_box::UnsyncStorage as generational_box::AnyStorage>::Mut<'a, T>;
280
281impl<T> State<T> {
282    /// Read the current value and subscribe the current component to changes.
283    ///
284    /// When the state value changes, any component or hook that has called `read()` will re-render.
285    ///
286    /// # Example
287    ///
288    /// ```rust,no_run
289    /// # use freya::prelude::*;
290    /// let count = use_state(|| 0);
291    /// let current_value = count.read();
292    /// ```
293    pub fn read(&self) -> ReadRef<'static, T> {
294        if let Some(mut rc) = ReactiveContext::try_current() {
295            rc.subscribe(&self.subscribers.read());
296        }
297        self.key.read()
298    }
299
300    /// Read the current value without subscribing to changes.
301    ///
302    /// This method provides access to the current state value without registering the current
303    /// component as a subscriber. The component will **not** re-render if the state changes.
304    ///
305    /// # When to Use
306    ///
307    /// Use `peek()` when you need to read the state value for a one-off operation where
308    /// reactivity is not needed, such as:
309    /// - Comparisons for conditional updates
310    /// - Debugging/logging
311    /// - Initial value checks
312    ///
313    /// # Example
314    ///
315    /// ```rust,no_run
316    /// # use freya::prelude::*;
317    /// let count = use_state(|| 0);
318    ///
319    /// // Check if count is zero without subscribing
320    /// if *count.peek() == 0 {
321    ///     println!("Count is still zero");
322    /// }
323    ///
324    /// // For reactive reading, use `read()` instead:
325    /// let display_text = format!("Count: {}", count.read());
326    /// ```
327    ///
328    /// # Performance Note
329    ///
330    /// Prefer `read()` over `peek()` unless you specifically need non-reactive access.
331    pub fn peek(&self) -> ReadRef<'static, T> {
332        self.key.read()
333    }
334
335    /// Get a mutable reference to the state value and notify subscribers.
336    ///
337    /// This method returns a `WriteRef<T>` that allows direct mutation of the state value.
338    /// All subscribed components will be notified and will re-render on the next frame.
339    ///
340    /// # Example
341    ///
342    /// ```rust,no_run
343    /// # use freya::prelude::*;
344    /// let mut count = use_state(|| 0);
345    ///
346    /// // Direct mutation
347    /// *count.write() += 1;
348    ///
349    /// // Multiple operations
350    /// {
351    ///     let mut value = count.write();
352    ///     *value *= 2;
353    ///     *value += 10;
354    /// } // Subscribers notified here
355    /// ```
356    ///
357    /// # See Also
358    ///
359    /// - `with_mut()` for closure-based mutations
360    /// - `set()` for replacing the entire value
361    pub fn write(&mut self) -> WriteRef<'static, T> {
362        self.subscribers.write().borrow_mut().retain(|s| s.notify());
363        self.key.write()
364    }
365
366    /// Modify the state value using a closure and notify subscribers.
367    ///
368    /// This method provides a convenient way to mutate the state value using a closure,
369    /// automatically handling subscriber notification.
370    ///
371    /// # Example
372    ///
373    /// ```rust,no_run
374    /// # use freya::prelude::*;
375    /// let mut counter = use_state(|| 0);
376    ///
377    /// counter.with_mut(|mut value| {
378    ///     *value += 1;
379    ///     *value *= 2;
380    /// });
381    ///
382    /// // Equivalent to:
383    /// *counter.write() += 1;
384    /// *counter.write() *= 2;
385    /// // But more efficient (single notification)
386    /// ```
387    pub fn with_mut(&mut self, with: impl FnOnce(WriteRef<'static, T>))
388    where
389        T: 'static,
390    {
391        self.subscribers.write().borrow_mut().retain(|s| s.notify());
392        with(self.key.write());
393    }
394
395    /// Get a mutable reference without requiring a mutable borrow of the State.
396    ///
397    /// This is an advanced method that allows writing to the state without having
398    /// mutable access to the `State` itself. Use with caution as it bypasses Rust's
399    /// borrow checker guarantees.
400    ///
401    /// # Safety Considerations
402    ///
403    /// This method should only be used when you cannot obtain a mutable reference
404    /// to the `State` but still need to modify it. Prefer `write()` when possible.
405    pub fn write_unchecked(&self) -> WriteRef<'static, T> {
406        self.subscribers.write().borrow_mut().retain(|s| s.notify());
407        self.key.write()
408    }
409
410    /// Replace the current state value with a new one.
411    ///
412    /// This method completely replaces the existing value with the provided one
413    /// and notifies all subscribers.
414    ///
415    /// # Example
416    ///
417    /// ```rust,no_run
418    /// # use freya::prelude::*;
419    /// let mut status = use_state(|| "idle");
420    ///
421    /// // Replace the value
422    /// status.set("loading");
423    /// status.set("complete");
424    /// ```
425    ///
426    /// # See Also
427    ///
428    /// - `set_if_modified()` to avoid unnecessary updates when the value hasn't changed
429    pub fn set(&mut self, value: T)
430    where
431        T: 'static,
432    {
433        *self.write() = value;
434    }
435
436    /// Replace the state value only if it's different from the current value.
437    ///
438    /// This method compares the new value with the current value using `PartialEq`.
439    /// If they are different, it updates the state and notifies subscribers.
440    /// If they are the same, no update occurs.
441    ///
442    /// # Performance Benefits
443    ///
444    /// This prevents unnecessary re-renders when setting the same value repeatedly.
445    ///
446    /// # Example
447    ///
448    /// ```rust,no_run
449    /// # use freya::prelude::*;
450    /// let mut count = use_state(|| 0);
451    ///
452    /// // This will update and notify subscribers
453    /// count.set_if_modified(5);
454    ///
455    /// // This will do nothing (value is already 5)
456    /// count.set_if_modified(5);
457    /// ```
458    ///
459    /// # Requirements
460    ///
461    /// The type `T` must implement `PartialEq`.
462    pub fn set_if_modified(&mut self, value: T)
463    where
464        T: 'static + PartialEq,
465    {
466        let is_equal = *self.peek() == value;
467        if !is_equal {
468            self.set(value);
469        }
470    }
471
472    /// Replace the state value if modified and execute a callback.
473    ///
474    /// Similar to `set_if_modified()`, but also runs a callback function if the value
475    /// was actually changed.
476    ///
477    /// # Example
478    ///
479    /// ```rust,no_run
480    /// # use freya::prelude::*;
481    /// let mut score = use_state(|| 0);
482    ///
483    /// score.set_if_modified_and_then(100, || {
484    ///     println!("High score achieved!");
485    ///     // Trigger additional logic like saving to storage
486    /// });
487    /// ```
488    ///
489    /// # Use Cases
490    ///
491    /// - Logging state changes
492    /// - Triggering side effects only when value changes
493    /// - Analytics tracking
494    pub fn set_if_modified_and_then(&mut self, value: T, then: impl FnOnce())
495    where
496        T: 'static + PartialEq,
497    {
498        let is_equal = *self.peek() == value;
499        if !is_equal {
500            self.set(value);
501            then();
502        }
503    }
504
505    /// Create a new State attached to the current component's scope.
506    ///
507    /// This method creates a reactive state value that will be automatically cleaned up
508    /// when the current component unmounts.
509    ///
510    /// # Example
511    ///
512    /// ```rust,no_run
513    /// # use freya::prelude::*;
514    /// // Usually used through use_state() hook instead:
515    /// let count = use_state(|| 0);
516    ///
517    /// // Direct creation (rare):
518    /// let state = State::create(42);
519    /// ```
520    ///
521    /// # See Also
522    ///
523    /// - `use_state()` - The recommended way to create state in components
524    /// - `create_global()` - For application-wide state
525    pub fn create(value: T) -> Self
526    where
527        T: 'static, // TODO: Move this lifetime bound to impl
528    {
529        Self::create_in_scope(value, None)
530    }
531
532    /// Create a State attached to a specific scope.
533    ///
534    /// Advanced method for creating state in a different scope than the current one.
535    /// Pass `None` to attach to the current scope (same as `create()`).
536    ///
537    /// # Parameters
538    ///
539    /// - `value`: The initial value for the state
540    /// - `scope_id`: The scope to attach to, or `None` for current scope
541    ///
542    /// # Use Cases
543    ///
544    /// - Creating state in parent scopes
545    /// - Advanced component patterns
546    /// - Testing utilities
547    pub fn create_in_scope(value: T, scope_id: impl Into<Option<ScopeId>>) -> Self
548    where
549        T: 'static,
550    {
551        // TODO: Move this lifetime bound to impl
552        let owner = CurrentContext::with(|context| {
553            let scopes_storages = context.scopes_storages.borrow_mut();
554
555            let scopes_storage = scopes_storages.get(&scope_id.into().unwrap_or(context.scope_id));
556            scopes_storage.unwrap().owner.clone()
557        });
558        let key = owner.insert(value);
559        let subscribers = owner.insert(Rc::default());
560        State { key, subscribers }
561    }
562
563    /// Create a global State that persists for the entire application lifetime.
564    ///
565    /// This creates state that is not tied to any component scope and will live
566    /// until the application shuts down. Use sparingly as it can lead to memory leaks
567    /// if not managed carefully.
568    ///
569    /// # Warning
570    ///
571    /// Global state should be used judiciously. Prefer component-scoped state (`use_state()`)
572    /// or shared state (`freya-radio`) for most use cases.
573    ///
574    /// # Example
575    ///
576    /// ```rust,no_run
577    /// # use freya::prelude::*;
578    /// // Create global state in a function
579    /// fn create_global_config() -> State<i32> {
580    ///     State::create_global(42)
581    /// }
582    /// ```
583    ///
584    /// # Memory Management
585    ///
586    /// Global state is leaked using `Box::leak()` and will not be automatically cleaned up.
587    /// Ensure global state contains lightweight data or implement manual cleanup if needed.
588    pub fn create_global(value: T) -> Self
589    where
590        T: 'static,
591    {
592        let owner = UnsyncStorage::owner();
593        Box::leak(Box::new(owner.clone()));
594        let key = owner.insert(value);
595        let subscribers = owner.insert(Rc::default());
596        State { key, subscribers }
597    }
598
599    /// Subscribe the current reactive context to this state's changes.
600    pub(crate) fn subscribe(&self) {
601        if let Some(mut rc) = ReactiveContext::try_current() {
602            rc.subscribe(&self.subscribers.read());
603        }
604    }
605
606    /// Notify all subscribers that the state has changed.
607    pub(crate) fn notify(&self) {
608        self.subscribers.write().borrow_mut().retain(|s| s.notify());
609    }
610}
611
612impl<T> Clone for State<T> {
613    fn clone(&self) -> Self {
614        *self
615    }
616}
617
618impl<T> Copy for State<T> {}
619
620impl<T> State<Option<T>> {
621    /// Take ownership of the contained value, leaving `None` in its place.
622    ///
623    /// This method is only available for `State<Option<T>>` and moves the value
624    /// out of the state, replacing it with `None`.
625    ///
626    /// # Example
627    ///
628    /// ```rust,no_run
629    /// # use freya::prelude::*;
630    /// let mut maybe_value = use_state(|| Some("hello".to_string()));
631    ///
632    /// // Take the value, state becomes None
633    /// let taken = maybe_value.take(); // Some("hello")
634    /// assert_eq!(*maybe_value.read(), None);
635    /// ```
636    ///
637    /// # Use Cases
638    ///
639    /// - Moving values out of reactive state
640    /// - One-time consumption of optional state
641    /// - State transitions where the value is no longer needed
642    pub fn take(&mut self) -> Option<T>
643    where
644        T: 'static,
645    {
646        self.write().take()
647    }
648}
649/// Creates a reactive state value initialized with the returned value of the `init` callback.
650///
651/// This hook creates a `State<T>` that is automatically scoped to the current component.
652/// The state will be cleaned up when the component unmounts.
653///
654/// # Parameters
655///
656/// - `init`: A closure that returns the initial value for the state
657///
658/// # Type Requirements
659///
660/// The type `T` must be `'static` (no borrowed references).
661///
662/// # Example
663///
664/// ```rust,no_run
665/// # use freya::prelude::*;
666/// fn counter() -> impl IntoElement {
667///     let mut count = use_state(|| 0);
668///
669///     rect().child(format!("Count: {}", count.read())).child(
670///         Button::new()
671///             .child("Increment")
672///             .on_press(move |_| *count.write() += 1),
673///     )
674/// }
675/// ```
676///
677/// # Advanced Usage
678///
679/// ```rust,no_run
680/// # use freya::prelude::*;
681/// // Complex initialization
682/// let mut user_data = use_state(|| {
683///     // Expensive computation or data loading
684///     String::from("default_preferences")
685/// });
686/// ```
687///
688/// # See Also
689///
690/// - [`State`] for the reactive state type
691/// - `freya-radio` crate for global state management
692pub fn use_state<T: 'static>(init: impl FnOnce() -> T) -> State<T> {
693    use_hook(|| State::create(init()))
694}