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}