Skip to main content

react_rs_core/
effect.rs

1use crate::runtime::RUNTIME;
2
3pub fn create_effect<F>(f: F)
4where
5    F: Fn() + 'static,
6{
7    let effect_id = RUNTIME.with(|rt| rt.borrow_mut().register_effect(f));
8    run_effect(effect_id);
9}
10
11pub(crate) fn run_effect(id: usize) {
12    RUNTIME.with(|rt| {
13        let prev = rt.borrow_mut().set_current_effect(Some(id));
14
15        let effect_ptr: Option<*const dyn Fn()> = rt
16            .borrow()
17            .get_effect(id)
18            .map(|e| e.as_ref() as *const dyn Fn());
19
20        if let Some(effect) = effect_ptr {
21            unsafe { (*effect)() };
22        }
23
24        rt.borrow_mut().set_current_effect(prev);
25    });
26}
27
28pub(crate) fn flush_effects() {
29    loop {
30        let effect_id = RUNTIME.with(|rt| rt.borrow_mut().pop_pending_effect());
31        match effect_id {
32            Some(id) => run_effect(id),
33            None => break,
34        }
35    }
36}
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41    use crate::signal::create_signal;
42    use std::cell::RefCell;
43    use std::rc::Rc;
44
45    #[test]
46    fn test_effect_runs_immediately() {
47        let ran = Rc::new(RefCell::new(false));
48        let ran_clone = ran.clone();
49
50        create_effect(move || {
51            *ran_clone.borrow_mut() = true;
52        });
53
54        assert!(*ran.borrow());
55    }
56
57    #[test]
58    fn test_effect_auto_run() {
59        let (count, set_count) = create_signal(0);
60        let effect_ran = Rc::new(RefCell::new(0));
61        let effect_ran_clone = effect_ran.clone();
62
63        create_effect(move || {
64            let _ = count.get();
65            *effect_ran_clone.borrow_mut() += 1;
66        });
67
68        assert_eq!(*effect_ran.borrow(), 1);
69        set_count.set(1);
70        assert_eq!(*effect_ran.borrow(), 2);
71    }
72
73    #[test]
74    fn test_effect_only_runs_when_tracked_signal_changes() {
75        let (count, set_count) = create_signal(0);
76        let (other, _set_other) = create_signal(100);
77        let effect_ran = Rc::new(RefCell::new(0));
78        let effect_ran_clone = effect_ran.clone();
79
80        create_effect(move || {
81            let _ = count.get();
82            let _ = other.get_untracked();
83            *effect_ran_clone.borrow_mut() += 1;
84        });
85
86        assert_eq!(*effect_ran.borrow(), 1);
87
88        set_count.set(1);
89        assert_eq!(*effect_ran.borrow(), 2);
90
91        set_count.set(2);
92        assert_eq!(*effect_ran.borrow(), 3);
93    }
94
95    #[test]
96    fn test_multiple_effects() {
97        let (count, set_count) = create_signal(0);
98        let effect1_ran = Rc::new(RefCell::new(0));
99        let effect2_ran = Rc::new(RefCell::new(0));
100
101        let e1 = effect1_ran.clone();
102        let c1 = count.clone();
103        create_effect(move || {
104            let _ = c1.get();
105            *e1.borrow_mut() += 1;
106        });
107
108        let e2 = effect2_ran.clone();
109        create_effect(move || {
110            let _ = count.get();
111            *e2.borrow_mut() += 1;
112        });
113
114        assert_eq!(*effect1_ran.borrow(), 1);
115        assert_eq!(*effect2_ran.borrow(), 1);
116
117        set_count.set(1);
118
119        assert_eq!(*effect1_ran.borrow(), 2);
120        assert_eq!(*effect2_ran.borrow(), 2);
121    }
122}