Skip to content

Commit 6ce21d2

Browse files
committed
revision: debounce
1 parent e9db591 commit 6ce21d2

File tree

3 files changed

+88
-54
lines changed

3 files changed

+88
-54
lines changed

packages/time/src/debounce.rs

Lines changed: 86 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,32 @@
1-
use dioxus::prelude::*;
2-
use futures::{
3-
channel::mpsc::{self, UnboundedSender as Sender},
4-
StreamExt,
1+
use crate::{use_timeout, TimeoutHandle, UseTimeout};
2+
use dioxus::{
3+
dioxus_core::SpawnIfAsync,
4+
hooks::use_signal,
5+
signals::{Signal, Writable},
56
};
67
use std::time::Duration;
78

89
/// The interface for calling a debounce.
910
///
1011
/// See [`use_debounce`] for more information.
11-
pub struct UseDebounce<T: 'static> {
12-
sender: Signal<Sender<T>>,
12+
#[derive(Clone, Copy, PartialEq)]
13+
pub struct UseDebounce<Args: 'static> {
14+
current_handle: Signal<Option<TimeoutHandle>>,
15+
timeout: UseTimeout<Args>,
1316
}
1417

15-
impl<T> UseDebounce<T> {
18+
impl<Args> UseDebounce<Args> {
1619
/// Start the debounce countdown, resetting it if already started.
17-
pub fn action(&mut self, data: T) {
18-
self.sender.write().unbounded_send(data).ok();
20+
pub fn action(&mut self, args: Args) {
21+
self.cancel();
22+
self.current_handle.set(Some(self.timeout.action(args)));
1923
}
20-
}
21-
22-
// Manually implement Clone, Copy, and PartialEq as #[derive] thinks that T needs to implement these (it doesn't).
23-
impl<T> Clone for UseDebounce<T> {
24-
fn clone(&self) -> Self {
25-
*self
26-
}
27-
}
28-
29-
impl<T> Copy for UseDebounce<T> {}
3024

31-
impl<T> PartialEq for UseDebounce<T> {
32-
fn eq(&self, other: &Self) -> bool {
33-
self.sender == other.sender
25+
/// Cancel the debounce action.
26+
pub fn cancel(&mut self) {
27+
if let Some(handle) = self.current_handle.take() {
28+
handle.cancel();
29+
}
3430
}
3531
}
3632

@@ -41,54 +37,92 @@ impl<T> PartialEq for UseDebounce<T> {
4137
///
4238
/// # Examples
4339
///
40+
/// Example of using a debounce:
4441
/// ```rust
4542
/// use dioxus::prelude::*;
4643
/// use dioxus_time::use_debounce;
4744
/// use std::time::Duration;
4845
///
4946
/// #[component]
5047
/// fn App() -> Element {
48+
/// // Create a two second debounce.
49+
/// // This will print "ran" after two seconds since the last action call.
5150
/// let mut debounce = use_debounce(Duration::from_secs(2), |_| println!("ran"));
5251
///
5352
/// rsx! {
5453
/// button {
5554
/// onclick: move |_| {
55+
/// // Call the debounce.
5656
/// debounce.action(());
5757
/// },
5858
/// "Click!"
5959
/// }
6060
/// }
6161
/// }
6262
/// ```
63-
pub fn use_debounce<T>(time: Duration, cb: impl FnOnce(T) + Copy + 'static) -> UseDebounce<T> {
64-
use_hook(|| {
65-
let (sender, mut receiver) = mpsc::unbounded();
66-
let debouncer = UseDebounce {
67-
sender: Signal::new(sender),
68-
};
69-
70-
spawn(async move {
71-
let mut current_task: Option<Task> = None;
72-
73-
loop {
74-
if let Some(data) = receiver.next().await {
75-
if let Some(task) = current_task.take() {
76-
task.cancel();
77-
}
78-
79-
current_task = Some(spawn(async move {
80-
#[cfg(not(target_family = "wasm"))]
81-
tokio::time::sleep(time).await;
82-
83-
#[cfg(target_family = "wasm")]
84-
gloo_timers::future::sleep(time).await;
85-
86-
cb(data);
87-
}));
88-
}
89-
}
90-
});
63+
///
64+
/// #### Cancelling A Debounce
65+
/// If you need to cancel the currently active debounce, you can call [`UseDebounce::cancel`]:
66+
/// ```rust
67+
/// use dioxus::prelude::*;
68+
/// use dioxus_time::use_debounce;
69+
/// use std::time::Duration;
70+
///
71+
/// #[component]
72+
/// fn App() -> Element {
73+
/// let mut debounce = use_debounce(Duration::from_secs(5), |_| println!("ran"));
74+
///
75+
/// rsx! {
76+
/// button {
77+
/// // Start the debounce on click.
78+
/// onclick: move |_| debounce.action(()),
79+
/// "Action!"
80+
/// }
81+
/// button {
82+
/// // Cancel the debounce on click.
83+
/// onclick: move |_| debounce.cancel(),
84+
/// "Cancel!"
85+
/// }
86+
/// }
87+
/// }
88+
/// ```
89+
///
90+
/// ### Async Debounce
91+
/// Debounces can accept an async callback:
92+
/// ```rust
93+
/// use dioxus::prelude::*;
94+
/// use dioxus_time::use_debounce;
95+
/// use std::time::Duration;
96+
///
97+
/// #[component]
98+
/// fn App() -> Element {
99+
/// // Create a two second debounce that uses some async/await.
100+
/// let mut debounce = use_debounce(Duration::from_secs(2), |_| async {
101+
/// println!("debounce called!");
102+
/// tokio::time::sleep(Duration::from_secs(2)).await;
103+
/// println!("after async");
104+
/// });
105+
///
106+
/// rsx! {
107+
/// button {
108+
/// onclick: move |_| {
109+
/// // Call the debounce.
110+
/// debounce.action(());
111+
/// },
112+
/// "Click!"
113+
/// }
114+
/// }
115+
/// }
116+
/// ```
117+
pub fn use_debounce<Args: 'static, MaybeAsync: SpawnIfAsync<Marker>, Marker>(
118+
duration: Duration,
119+
callback: impl FnMut(Args) -> MaybeAsync + 'static,
120+
) -> UseDebounce<Args> {
121+
let timeout = use_timeout(duration, callback);
122+
let current_handle = use_signal(|| None);
91123

92-
debouncer
93-
})
124+
UseDebounce {
125+
timeout,
126+
current_handle,
127+
}
94128
}

packages/time/src/interval.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use dioxus::prelude::{use_hook, Callback, Writable};
22
use std::time::Duration;
33

4-
/// A handle to an interval.
4+
/// The interface to a debounce.
55
///
66
/// This handle allows you to cancel an interval.
77
#[derive(Clone, PartialEq, Copy)]

packages/time/src/timeout.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ impl<Args> PartialEq for UseTimeout<Args> {
5858
/// This handle allows you to cancel the timeout from triggering with [`TimeoutHandle::cancel`]
5959
///
6060
/// See [`use_timeout`] for more information.
61-
#[derive(Clone, Copy, PartialEq)]
61+
#[derive(Debug, Clone, Copy, PartialEq)]
6262
pub struct TimeoutHandle {
6363
handle: Task,
6464
}

0 commit comments

Comments
 (0)