Skip to main content

euv_core/reactive/effect/
struct.rs

1use crate::*;
2
3/// Inner state of a RenderEffect, holding the effect closure and dependency tracking.
4///
5/// Each RenderEffect tracks which signals it depends on, and when any of those
6/// signals change, the effect is re-executed automatically. Before each
7/// re-execution, old dependencies are cleaned up to prevent listener accumulation.
8#[derive(CustomDebug, Data, New)]
9pub(crate) struct RenderEffectInner {
10    /// The effect closure to re-execute when dependencies change.
11    #[debug(skip)]
12    #[get(pub(crate))]
13    #[set(pub(crate))]
14    pub(crate) effect_fn: Box<dyn FnMut()>,
15    /// Addresses of signals this effect currently depends on.
16    ///
17    /// Populated during `run_once` by `track_signal`. Before each
18    /// `run_once`, these dependencies are cleaned up (the effect's
19    /// subscriber entry is removed from each signal's registry entry).
20    #[get(pub(crate))]
21    #[set(pub(crate))]
22    pub(crate) dependencies: Vec<usize>,
23    /// Re-entrancy guard. Set to `true` while the effect closure is
24    /// executing. If `run_once` is called recursively while this is
25    /// `true`, the call is skipped to prevent infinite loops caused
26    /// by signals being set inside the effect closure.
27    #[get(pub(crate), type(copy))]
28    #[set(pub(crate))]
29    pub(crate) running: bool,
30    /// Whether this effect has been disposed and should no longer execute.
31    ///
32    /// When a `RenderEffect` is disposed, `run_once` becomes a no-op and
33    /// `cleanup_effect_dependencies` is called to remove all subscriber
34    /// entries from the global registry. This prevents stale effects from
35    /// being scheduled or executed after their associated DOM nodes have
36    /// been removed (e.g., during match arm switches in routing).
37    #[get(pub(crate), type(copy))]
38    #[set(pub(crate))]
39    pub(crate) disposed: bool,
40}
41
42/// A reactive effect that automatically tracks signal dependencies and
43/// re-executes when any dependency changes.
44///
45/// Unlike the global `__euv_signal_update__` mechanism which broadcasts to
46/// all DynamicNodes, a RenderEffect only re-runs when signals it actually
47/// reads have changed, enabling fine-grained reactive updates.
48#[derive(Clone, CustomDebug, Data, New)]
49pub struct RenderEffect {
50    /// Shared reference to the heap-allocated inner state.
51    #[debug(skip)]
52    #[get(pub(crate))]
53    #[set(pub(crate))]
54    pub(crate) inner: Rc<RefCell<RenderEffectInner>>,
55}
56
57/// A `Sync` wrapper for single-threaded global `HashMap` access.
58///
59/// SAFETY: This type is only safe to use in single-threaded contexts
60/// (e.g., WASM). It implements `Sync` to allow usage as a `static mut`
61/// variable, but concurrent access from multiple threads would be
62/// undefined behavior.
63#[derive(Data, Debug, New)]
64pub(crate) struct EffectSubscribersCell(
65    /// Interior-mutable storage for the effect subscribers registry.
66    #[get(pub(crate))]
67    #[set(pub(crate))]
68    pub(crate) UnsafeCell<Option<HashMap<usize, Vec<usize>>>>,
69);
70
71/// A `Sync` wrapper for single-threaded global `Option<usize>` access.
72///
73/// SAFETY: This type is only safe to use in single-threaded contexts
74/// (e.g., WASM). It implements `Sync` to allow usage as a `static mut`
75/// variable, but concurrent access from multiple threads would be
76/// undefined behavior.
77#[derive(Data, Debug, New)]
78pub(crate) struct CurrentEffectCell(
79    /// Interior-mutable storage for the current effect address.
80    #[get(pub(crate))]
81    #[set(pub(crate))]
82    pub(crate) UnsafeCell<Option<usize>>,
83);
84
85/// A `Sync` wrapper for single-threaded global `HashMap` access.
86///
87/// SAFETY: This type is only safe to use in single-threaded contexts
88/// (e.g., WASM). It implements `Sync` to allow usage as a `static mut`
89/// variable, but concurrent access from multiple threads would be
90/// undefined behavior.
91#[derive(Data, Debug, New)]
92pub(crate) struct RenderEffectRegistryCell(
93    /// Interior-mutable storage for the render effect registry.
94    #[get(pub(crate))]
95    #[set(pub(crate))]
96    pub(crate) UnsafeCell<Option<HashMap<usize, Rc<RefCell<RenderEffectInner>>>>>,
97);