dioxus_sdk_time/debounce.rs
1use crate::{TimeoutHandle, UseTimeout, use_timeout};
2use dioxus::{
3 dioxus_core::SpawnIfAsync,
4 hooks::use_signal,
5 signals::{Signal, WritableExt as _},
6};
7use std::time::Duration;
8
9/// The interface for calling a debounce.
10///
11/// See [`use_debounce`] for more information.
12pub struct UseDebounce<Args: 'static> {
13 current_handle: Signal<Option<TimeoutHandle>>,
14 timeout: UseTimeout<Args>,
15}
16
17impl<Args> UseDebounce<Args> {
18 /// Start the debounce countdown, resetting it if already started.
19 pub fn action(&mut self, args: Args) {
20 self.cancel();
21 self.current_handle.set(Some(self.timeout.action(args)));
22 }
23
24 /// Cancel the debounce action.
25 pub fn cancel(&mut self) {
26 if let Some(handle) = self.current_handle.take() {
27 handle.cancel();
28 }
29 }
30}
31
32impl<Args> Clone for UseDebounce<Args> {
33 fn clone(&self) -> Self {
34 *self
35 }
36}
37impl<Args> Copy for UseDebounce<Args> {}
38impl<Args> PartialEq for UseDebounce<Args> {
39 fn eq(&self, other: &Self) -> bool {
40 self.current_handle == other.current_handle && self.timeout == other.timeout
41 }
42}
43
44/// A hook for allowing a function to be called only after a provided [`Duration`] has passed.
45///
46/// Once the [`UseDebounce::action`] method is called, a timer will start counting down until
47/// the callback is ran. If the [`UseDebounce::action`] method is called again, the timer will restart.
48///
49/// # Examples
50///
51/// Example of using a debounce:
52/// ```rust
53/// use dioxus::prelude::*;
54/// use dioxus_sdk_time::use_debounce;
55/// use std::time::Duration;
56///
57/// #[component]
58/// fn App() -> Element {
59/// // Create a two second debounce.
60/// // This will print "ran" after two seconds since the last action call.
61/// let mut debounce = use_debounce(Duration::from_secs(2), |_| println!("ran"));
62///
63/// rsx! {
64/// button {
65/// onclick: move |_| {
66/// // Call the debounce.
67/// debounce.action(());
68/// },
69/// "Click!"
70/// }
71/// }
72/// }
73/// ```
74///
75/// #### Cancelling A Debounce
76/// If you need to cancel the currently active debounce, you can call [`UseDebounce::cancel`]:
77/// ```rust
78/// use dioxus::prelude::*;
79/// use dioxus_sdk_time::use_debounce;
80/// use std::time::Duration;
81///
82/// #[component]
83/// fn App() -> Element {
84/// let mut debounce = use_debounce(Duration::from_secs(5), |_| println!("ran"));
85///
86/// rsx! {
87/// button {
88/// // Start the debounce on click.
89/// onclick: move |_| debounce.action(()),
90/// "Action!"
91/// }
92/// button {
93/// // Cancel the debounce on click.
94/// onclick: move |_| debounce.cancel(),
95/// "Cancel!"
96/// }
97/// }
98/// }
99/// ```
100///
101/// ### Async Debounce
102/// Debounces can accept an async callback:
103/// ```rust
104/// use dioxus::prelude::*;
105/// use dioxus_sdk_time::use_debounce;
106/// use std::time::Duration;
107///
108/// #[component]
109/// fn App() -> Element {
110/// // Create a two second debounce that uses some async/await.
111/// let mut debounce = use_debounce(Duration::from_secs(2), |_| async {
112/// println!("debounce called!");
113/// tokio::time::sleep(Duration::from_secs(2)).await;
114/// println!("after async");
115/// });
116///
117/// rsx! {
118/// button {
119/// onclick: move |_| {
120/// // Call the debounce.
121/// debounce.action(());
122/// },
123/// "Click!"
124/// }
125/// }
126/// }
127/// ```
128pub fn use_debounce<Args: 'static, MaybeAsync: SpawnIfAsync<Marker>, Marker>(
129 duration: Duration,
130 callback: impl FnMut(Args) -> MaybeAsync + 'static,
131) -> UseDebounce<Args> {
132 let timeout = use_timeout(duration, callback);
133 let current_handle = use_signal(|| None);
134
135 UseDebounce {
136 timeout,
137 current_handle,
138 }
139}