freya_core/lifecycle/
effect.rs

1use std::{
2    cell::RefCell,
3    rc::Rc,
4};
5
6use crate::{
7    prelude::{
8        State,
9        spawn,
10        use_hook,
11        use_reactive,
12    },
13    reactive_context::ReactiveContext,
14};
15
16pub struct Effect;
17
18impl Effect {
19    pub fn create(mut callback: impl FnMut() + 'static) {
20        let (rx, rc) = ReactiveContext::new_for_task();
21        spawn(async move {
22            loop {
23                ReactiveContext::run(rc.clone(), &mut callback);
24                rx.notified().await;
25            }
26        });
27    }
28
29    // TODO: This should probably not be sync but instead Freya should prioritize effect tasks
30    pub fn create_sync_with_gen(mut callback: impl FnMut(usize) + 'static) {
31        let (rx, rc) = ReactiveContext::new_for_task();
32        ReactiveContext::run(rc.clone(), || callback(0));
33        spawn(async move {
34            let mut current_gen = 1;
35            loop {
36                rx.notified().await;
37                ReactiveContext::run(rc.clone(), || callback(current_gen));
38                current_gen += 1;
39            }
40        });
41    }
42
43    pub fn create_sync(mut callback: impl FnMut() + 'static) {
44        let (rx, rc) = ReactiveContext::new_for_task();
45        ReactiveContext::run(rc.clone(), &mut callback);
46        spawn(async move {
47            loop {
48                rx.notified().await;
49                ReactiveContext::run(rc.clone(), &mut callback);
50            }
51        });
52    }
53
54    pub fn create_after(callback: impl FnMut() + 'static) {
55        let (rx, rc) = ReactiveContext::new_for_task();
56        let callback = Rc::new(RefCell::new(callback));
57        spawn(async move {
58            loop {
59                let callback = callback.clone();
60                let rc = rc.clone();
61                spawn(async move {
62                    ReactiveContext::run(rc, &mut *callback.borrow_mut());
63                });
64                rx.notified().await;
65            }
66        });
67    }
68
69    pub fn create_value<T: 'static>(mut callback: impl FnMut() -> T + 'static) -> State<T> {
70        let (rx, rc) = ReactiveContext::new_for_task();
71        let mut state = State::create(ReactiveContext::run(rc.clone(), &mut callback));
72        spawn(async move {
73            let mut current_gen = 0;
74            loop {
75                if current_gen > 0 {
76                    state.set(ReactiveContext::run(rc.clone(), &mut callback));
77                }
78                rx.notified().await;
79                current_gen += 1;
80            }
81        });
82        state
83    }
84}
85
86/// Registers a callback that will run every time a [crate::lifecycle::state::State] which was [crate::lifecycle::state::State::read] inside, changes.
87pub fn use_side_effect(callback: impl FnMut() + 'static) {
88    use_hook(|| Effect::create(callback));
89}
90
91pub fn use_after_side_effect(callback: impl FnMut() + 'static) {
92    use_hook(|| Effect::create_after(callback));
93}
94
95pub fn use_side_effect_value<T: 'static>(callback: impl FnMut() -> T + 'static) -> State<T> {
96    use_hook(|| Effect::create_value(callback))
97}
98
99pub fn use_side_effect_with_deps<D: 'static + Clone + PartialEq>(
100    deps: &D,
101    mut callback: impl FnMut(&D) + 'static,
102) {
103    let deps = use_reactive(deps);
104    use_hook(move || {
105        Effect::create(move || {
106            let deps = deps.read();
107            callback(&deps)
108        })
109    });
110}