Skip to main content

dioxus_sdk_time/
interval.rs

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