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}