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}