leptos_use/
watch_pausable.rs

1use crate::{WatchOptions, watch_with_options};
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/// > Make sure you follow the [instructions in Server-Side Rendering](https://leptos-use.rs/server_side_rendering.html).
50///
51/// On the server this works just fine except if you throttle or debounce in which case the callback
52/// will never be called except if you set `immediate` to `true` in which case the callback will be
53/// called exactly once.
54///
55/// ## See also
56///
57/// * `leptos::watch`
58pub fn watch_pausable<W, T, DFn, CFn>(
59    deps: DFn,
60    callback: CFn,
61) -> WatchPausableReturn<
62    impl Fn() + Clone + Send + Sync,
63    impl Fn() + Clone + Send + Sync,
64    impl Fn() + Clone + Send + Sync,
65>
66where
67    DFn: Fn() -> W + 'static,
68    CFn: Fn(&W, Option<&W>, Option<T>) -> T + Clone + 'static,
69    W: Clone + 'static,
70    T: Clone + 'static,
71{
72    watch_pausable_with_options(deps, callback, WatchOptions::default())
73}
74
75/// Version of `watch_pausable` that accepts `WatchOptions`. See [`watch_pausable`] for how to use.
76pub fn watch_pausable_with_options<W, T, DFn, CFn>(
77    deps: DFn,
78    callback: CFn,
79    options: WatchOptions,
80) -> WatchPausableReturn<
81    impl Fn() + Clone + Send + Sync,
82    impl Fn() + Clone + Send + Sync,
83    impl Fn() + Clone + Send + Sync,
84>
85where
86    DFn: Fn() -> W + 'static,
87    CFn: Fn(&W, Option<&W>, Option<T>) -> T + Clone + 'static,
88    W: Clone + 'static,
89    T: Clone + 'static,
90{
91    let (is_active, set_active) = signal(true);
92
93    let pausable_callback = move |val: &W, prev_val: Option<&W>, prev_ret: Option<Option<T>>| {
94        if is_active.get_untracked() {
95            Some(callback(val, prev_val, prev_ret.unwrap_or(None)))
96        } else {
97            None
98        }
99    };
100
101    let stop = watch_with_options(deps, pausable_callback, options);
102
103    let pause = move || {
104        set_active.set(false);
105    };
106
107    let resume = move || {
108        set_active.set(true);
109    };
110
111    WatchPausableReturn {
112        stop,
113        pause,
114        resume,
115        is_active: is_active.into(),
116    }
117}
118
119/// Return type of [`watch_pausable`]
120pub struct WatchPausableReturn<StopFn, PauseFn, ResumeFn>
121where
122    StopFn: Fn() + Clone + Send + Sync,
123    PauseFn: Fn() + Clone + Send + Sync,
124    ResumeFn: Fn() + Clone + Send + Sync,
125{
126    /// Stops the watcher
127    pub stop: StopFn,
128
129    /// Pauses the watcher
130    pub pause: PauseFn,
131
132    /// Resumes the watcher
133    pub resume: ResumeFn,
134
135    /// Whether the watcher is active (not paused). This doesn't reflect if the watcher has been stopped
136    pub is_active: Signal<bool>,
137}