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);