Skip to main content

freya_core/lifecycle/
effect.rs

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