Skip to main content

euv_core/reactive/effect/
impl.rs

1use crate::*;
2
3/// Implementation of RenderEffect reactive operations.
4impl RenderEffect {
5    /// Creates a new RenderEffect that wraps the given closure.
6    ///
7    /// On creation, the closure is executed once immediately. During execution,
8    /// any `Signal::get()` calls will register this effect as a subscriber.
9    /// When any of those signals change, the closure is re-executed.
10    ///
11    /// # Arguments
12    ///
13    /// - `F` - The closure to execute reactively.
14    ///
15    /// # Returns
16    ///
17    /// - `Self` - A new RenderEffect handle.
18    pub fn create<F>(effect_fn: F) -> Self
19    where
20        F: FnMut() + 'static,
21    {
22        let effect_inner: Rc<RefCell<RenderEffectInner>> = Rc::new(RefCell::new(
23            RenderEffectInner::new(Box::new(effect_fn), Vec::new(), false, false),
24        ));
25        let effect: RenderEffect = RenderEffect::new(effect_inner.clone());
26        let addr: usize = effect.get_inner_addr();
27        render_effect_registry_mut().insert(addr, effect_inner);
28        effect.run_once();
29        effect
30    }
31
32    /// Returns the memory address of the inner `Rc` for identity comparison.
33    ///
34    /// # Returns
35    ///
36    /// - `usize` - The memory address of the inner `Rc`.
37    pub(crate) fn get_inner_addr(&self) -> usize {
38        Rc::as_ptr(self.get_inner()) as usize
39    }
40
41    /// Executes the effect closure once with dependency tracking.
42    ///
43    /// Before executing, removes this effect's subscriber entries from
44    /// all previously tracked signals (old dependencies) via the global
45    /// effect subscriber registry. Then sets this effect as the current
46    /// tracking target and executes the closure. During execution,
47    /// `Signal::get()` calls register this effect as a subscriber on
48    /// those signals via `track_signal`. After execution, the previous
49    /// tracking target is restored.
50    pub(crate) fn run_once(&self) {
51        let effect_addr: usize = self.get_inner_addr();
52        {
53            let mut inner: RefMut<RenderEffectInner> = self.get_inner().borrow_mut();
54            if inner.get_disposed() {
55                return;
56            }
57            if inner.get_running() {
58                return;
59            }
60            inner.set_running(true);
61            cleanup_effect_dependencies(effect_addr, inner.get_mut_dependencies());
62        }
63        let prev: Option<usize> = swap_current_effect(Some(effect_addr));
64        {
65            let mut inner: RefMut<RenderEffectInner> = self.get_inner().borrow_mut();
66            (inner.get_mut_effect_fn())();
67        }
68        set_current_effect(prev);
69        {
70            let mut inner: RefMut<RenderEffectInner> = self.get_inner().borrow_mut();
71            inner.set_running(false);
72        }
73    }
74
75    /// Marks this effect as disposed and cleans up all its dependencies.
76    ///
77    /// After disposal, `run_once` becomes a complete no-op. All subscriber
78    /// entries for this effect are removed from the global `EFFECT_SUBSCRIBERS`
79    /// registry, preventing the effect from being scheduled or executed again.
80    /// This is called when the associated DOM node is removed from the tree
81    /// (e.g., during a match arm switch in routing) to prevent stale effects
82    /// from causing infinite loops.
83    pub fn dispose(&self) {
84        let effect_addr: usize = self.get_inner_addr();
85        let mut inner: RefMut<RenderEffectInner> = self.get_inner().borrow_mut();
86        if inner.get_disposed() {
87            return;
88        }
89        inner.set_disposed(true);
90        cleanup_effect_dependencies(effect_addr, inner.get_mut_dependencies());
91    }
92}
93
94/// SAFETY: `EffectSubscribersCell` is only used in single-threaded WASM contexts.
95unsafe impl Sync for EffectSubscribersCell {}
96
97/// SAFETY: `CurrentEffectCell` is only used in single-threaded WASM contexts.
98unsafe impl Sync for CurrentEffectCell {}
99
100/// SAFETY: `RenderEffectRegistryCell` is only used in single-threaded WASM contexts.
101unsafe impl Sync for RenderEffectRegistryCell {}