Skip to main content

react_rs_core/
effect.rs

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