Skip to main content

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}