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}