sycamore_reactive/
utils.rs

1//! A few handy utilities.
2
3use crate::*;
4
5/// A trait that is implemented for reactive data that can be tracked, such as [`Signal`].
6///
7/// Also implemented for tuples containing `Trackable`s.
8pub trait Trackable {
9    /// Track the data reactively.
10    fn _track(&self);
11}
12
13impl<T> Trackable for Signal<T> {
14    fn _track(&self) {
15        self.track();
16    }
17}
18
19impl<T> Trackable for ReadSignal<T> {
20    fn _track(&self) {
21        self.track();
22    }
23}
24
25impl<T: Into<Self>> Trackable for MaybeDyn<T> {
26    fn _track(&self) {
27        match self {
28            MaybeDyn::Static(_) => {}
29            MaybeDyn::Signal(signal) => signal.track(),
30            MaybeDyn::Derived(f) => f()._track(),
31        }
32    }
33}
34
35macro_rules! impl_trackable_deps_for_tuple {
36    ($($T:tt),*) => {
37        paste::paste! {
38            impl<$($T,)*> Trackable for ($($T,)*)
39            where
40                $($T: Trackable,)*
41            {
42                fn _track(&self) {
43                    let ($([<$T:lower>],)*) = self;
44                    $(
45                        [<$T:lower>]._track();
46                    )*
47                }
48            }
49        }
50    }
51}
52
53impl_trackable_deps_for_tuple!(A);
54impl_trackable_deps_for_tuple!(A, B);
55impl_trackable_deps_for_tuple!(A, B, C);
56impl_trackable_deps_for_tuple!(A, B, C, D);
57impl_trackable_deps_for_tuple!(A, B, C, D, E);
58impl_trackable_deps_for_tuple!(A, B, C, D, E, F);
59impl_trackable_deps_for_tuple!(A, B, C, D, E, F, G);
60impl_trackable_deps_for_tuple!(A, B, C, D, E, F, G, H);
61impl_trackable_deps_for_tuple!(A, B, C, D, E, F, G, H, I);
62impl_trackable_deps_for_tuple!(A, B, C, D, E, F, G, H, I, J);
63impl_trackable_deps_for_tuple!(A, B, C, D, E, F, G, H, I, J, K);
64impl_trackable_deps_for_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
65
66/// A helper function for making dependencies explicit. This will create a closure that tracks
67/// those and only those dependencies that are passed to it.
68///
69/// # Params
70/// * `deps` - A list of signals/memos that are tracked. This can be a single signal or it can be a
71///   tuple of signals.
72/// * `f` - The callback function. Any dependencies that are accessed in here will not be tracked.
73///
74/// # Example
75/// ```
76/// # use sycamore_reactive::*;
77/// # create_root(|| {
78/// let state = create_signal(0);
79///
80/// create_effect(on(state, move || {
81///     println!("State changed. New state value = {}", state.get());
82/// }));
83/// // Prints "State changed. New state value = 0"
84///
85/// state.set(1);
86/// // Prints "State changed. New state value = 1"
87/// # });
88/// ```
89pub fn on<T>(
90    deps: impl Trackable + 'static,
91    mut f: impl FnMut() -> T + 'static,
92) -> impl FnMut() -> T + 'static {
93    move || {
94        deps._track();
95        untrack(&mut f)
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn on_untracks_callback() {
105        let _ = create_root(move || {
106            let trigger1 = create_signal(());
107            let trigger2 = create_signal(());
108
109            let mut counter = create_signal(0);
110            create_effect(on(trigger1, move || {
111                // This should not be tracked by the effect since it is inside the `on` callback.
112                trigger2.track();
113                counter += 1;
114            }));
115
116            assert_eq!(counter.get(), 1);
117            trigger1.set(());
118            assert_eq!(counter.get(), 2);
119            trigger2.set(());
120            assert_eq!(counter.get(), 2);
121        });
122    }
123}