leptos_use/
watch_pausable.rs

1use crate::{watch_with_options, WatchOptions};
2use leptos::prelude::*;
3
4/// Pausable [`watch`].
5///
6/// ## Demo
7///
8/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/watch_pausable)
9///
10/// ## Usage
11///
12/// ```
13/// # use leptos::prelude::*;
14/// # use leptos::logging::log;
15/// # use leptos_use::{watch_pausable, WatchPausableReturn};
16/// #
17/// # pub fn Demo() -> impl IntoView {
18/// let (source, set_source) = signal("foo".to_string());
19///
20/// let WatchPausableReturn {
21///     stop,
22///     pause,
23///     resume,
24///     ..
25/// } = watch_pausable(
26///     move || source.get(),
27///     |v, _, _| {
28///         log!("Changed to {}", v);
29///     },
30/// );
31///
32/// set_source.set("bar".to_string()); // > "Changed to bar"
33///
34/// pause();
35///
36/// set_source.set("foobar".to_string()); // (nothing happens)
37///
38/// resume();
39///
40/// set_source.set("hello".to_string()); // > "Changed to hello"
41/// #    view! { }
42/// # }
43/// ```
44///
45/// There's also [`watch_pausable_with_options`] which takes the same options as [`watch`].
46///
47/// ## Server-Side Rendering
48///
49/// On the server this works just fine except if you throttle or debounce in which case the callback
50/// will never be called except if you set `immediate` to `true` in which case the callback will be
51/// called exactly once.
52///
53/// ## See also
54///
55/// * `leptos::watch`
56pub fn watch_pausable<W, T, DFn, CFn>(
57    deps: DFn,
58    callback: CFn,
59) -> WatchPausableReturn<
60    impl Fn() + Clone + Send + Sync,
61    impl Fn() + Clone + Send + Sync,
62    impl Fn() + Clone + Send + Sync,
63>
64where
65    DFn: Fn() -> W + 'static,
66    CFn: Fn(&W, Option<&W>, Option<T>) -> T + Clone + 'static,
67    W: Clone + 'static,
68    T: Clone + 'static,
69{
70    watch_pausable_with_options(deps, callback, WatchOptions::default())
71}
72
73/// Version of `watch_pausable` that accepts `WatchOptions`. See [`watch_pausable`] for how to use.
74pub fn watch_pausable_with_options<W, T, DFn, CFn>(
75    deps: DFn,
76    callback: CFn,
77    options: WatchOptions,
78) -> WatchPausableReturn<
79    impl Fn() + Clone + Send + Sync,
80    impl Fn() + Clone + Send + Sync,
81    impl Fn() + Clone + Send + Sync,
82>
83where
84    DFn: Fn() -> W + 'static,
85    CFn: Fn(&W, Option<&W>, Option<T>) -> T + Clone + 'static,
86    W: Clone + 'static,
87    T: Clone + 'static,
88{
89    let (is_active, set_active) = signal(true);
90
91    let pausable_callback = move |val: &W, prev_val: Option<&W>, prev_ret: Option<Option<T>>| {
92        if is_active.get_untracked() {
93            Some(callback(val, prev_val, prev_ret.unwrap_or(None)))
94        } else {
95            None
96        }
97    };
98
99    let stop = watch_with_options(deps, pausable_callback, options);
100
101    let pause = move || {
102        set_active.set(false);
103    };
104
105    let resume = move || {
106        set_active.set(true);
107    };
108
109    WatchPausableReturn {
110        stop,
111        pause,
112        resume,
113        is_active: is_active.into(),
114    }
115}
116
117/// Return type of [`watch_pausable`]
118pub struct WatchPausableReturn<StopFn, PauseFn, ResumeFn>
119where
120    StopFn: Fn() + Clone + Send + Sync,
121    PauseFn: Fn() + Clone + Send + Sync,
122    ResumeFn: Fn() + Clone + Send + Sync,
123{
124    /// Stops the watcher
125    pub stop: StopFn,
126
127    /// Pauses the watcher
128    pub pause: PauseFn,
129
130    /// Resumes the watcher
131    pub resume: ResumeFn,
132
133    /// Whether the watcher is active (not paused). This doesn't reflect if the watcher has been stopped
134    pub is_active: Signal<bool>,
135}