Skip to main content

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