dioxus_time/interval.rs
1use dioxus::{
2 dioxus_core::SpawnIfAsync,
3 prelude::{Callback, Task, Writable, spawn, use_hook},
4 signals::Signal,
5};
6use std::time::Duration;
7
8/// The interface to a debounce.
9///
10/// You can cancel an interval with [`UseInterval::cancel`].
11/// See [`use_interval`] for more information.
12#[derive(Clone, PartialEq, Copy)]
13pub struct UseInterval {
14 inner: Signal<InnerUseInterval>,
15}
16
17struct InnerUseInterval {
18 pub(crate) interval: Option<Task>,
19}
20
21impl Drop for InnerUseInterval {
22 fn drop(&mut self) {
23 if let Some(interval) = self.interval.take() {
24 interval.cancel();
25 }
26 }
27}
28
29impl UseInterval {
30 /// Cancel the interval.
31 pub fn cancel(&mut self) {
32 if let Some(interval) = self.inner.write().interval.take() {
33 interval.cancel();
34 }
35 }
36}
37
38/// Repeatedly call a function at a specific interval.
39///
40/// Intervals are cancelable with the [`UseInterval::cancel`] method.
41///
42/// # Examples
43///
44/// Example of using an interval:
45/// ```rust
46/// use dioxus::prelude::*;
47/// use dioxus_time::use_interval;
48/// use std::time::Duration;
49///
50/// #[component]
51/// fn App() -> Element {
52/// let mut time_elapsed = use_signal(|| 0);
53/// // Create an interval that increases the time elapsed signal by one every second.
54/// use_interval(Duration::from_secs(1), move |()| time_elapsed += 1);
55///
56/// rsx! {
57/// "It has been {time_elapsed} since the app started."
58/// }
59/// }
60/// ```
61///
62/// #### Cancelling Intervals
63/// Example of cancelling an interval:
64/// ```rust
65/// use dioxus::prelude::*;
66/// use dioxus_time::use_interval;
67/// use std::time::Duration;
68///
69/// #[component]
70/// fn App() -> Element {
71/// let mut time_elapsed = use_signal(|| 0);
72/// let mut interval = use_interval(Duration::from_secs(1), move |()| time_elapsed += 1);
73///
74/// rsx! {
75/// "It has been {time_elapsed} since the app started."
76/// button {
77/// // Cancel the interval when the button is clicked.
78/// onclick: move |_| interval.cancel(),
79/// "Cancel Interval"
80/// }
81/// }
82/// }
83/// ```
84///
85/// #### Async Intervals
86/// Intervals can accept an async callback:
87/// ```rust
88/// use dioxus::prelude::*;
89/// use dioxus_time::use_interval;
90/// use std::time::Duration;
91///
92/// #[component]
93/// fn App() -> Element {
94/// let mut time_elapsed = use_signal(|| 0);
95/// // Create an interval that increases the time elapsed signal by one every second.
96/// use_interval(Duration::from_secs(1), move |()| async move {
97/// time_elapsed += 1;
98/// // Pretend we're doing some async work.
99/// tokio::time::sleep(Duration::from_secs(1)).await;
100/// println!("Done!");
101/// });
102///
103/// rsx! {
104/// "It has been {time_elapsed} since the app started."
105/// }
106/// }
107/// ```
108pub fn use_interval<MaybeAsync: SpawnIfAsync<Marker>, Marker>(
109 period: Duration,
110 callback: impl FnMut(()) -> MaybeAsync + 'static,
111) -> UseInterval {
112 let inner = use_hook(|| {
113 let callback = Callback::new(callback);
114
115 let task = spawn(async move {
116 #[cfg(not(target_family = "wasm"))]
117 let mut interval = tokio::time::interval(period);
118
119 loop {
120 #[cfg(not(target_family = "wasm"))]
121 interval.tick().await;
122
123 #[cfg(target_family = "wasm")]
124 {
125 gloo_timers::future::sleep(period).await;
126 }
127
128 callback.call(());
129 }
130 });
131
132 Signal::new(InnerUseInterval {
133 interval: Some(task),
134 })
135 });
136
137 UseInterval { inner }
138}