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}