leptos_use/
use_interval.rs

1use crate::utils::Pausable;
2use crate::{UseIntervalFnOptions, sendwrap_fn, use_interval_fn_with_options};
3use default_struct_builder::DefaultBuilder;
4use leptos::prelude::*;
5use leptos::reactive::wrappers::read::Signal;
6use std::rc::Rc;
7
8/// Reactive counter increases on every interval.
9///
10/// ## Demo
11///
12/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_interval)
13///
14/// ## Usage
15///
16/// ```
17/// # use leptos::prelude::*;
18/// # use leptos_use::{use_interval, UseIntervalReturn};
19/// #
20/// # #[component]
21/// # fn Demo() -> impl IntoView {
22/// let UseIntervalReturn {
23///     counter,
24///     reset,
25///     is_active,
26///     pause,
27///     resume
28/// }  = use_interval( 200 );
29/// # view! { }
30/// # }
31/// ```
32///
33/// ## SendWrapped Return
34///
35/// The returned closures `pause`, `resume` and `reset` are sendwrapped functions. They can
36/// only be called from the same thread that called `use_intersection_observer`.
37///
38/// ## Server-Side Rendering
39///
40/// > Make sure you follow the [instructions in Server-Side Rendering](https://leptos-use.rs/server_side_rendering.html).
41///
42/// On the server this function will simply be ignored.
43pub fn use_interval<N>(
44    interval: N,
45) -> UseIntervalReturn<
46    impl Fn() + Clone + Send + Sync,
47    impl Fn() + Clone + Send + Sync,
48    impl Fn() + Clone + Send + Sync,
49>
50where
51    N: Into<Signal<u64>>,
52{
53    use_interval_with_options(interval, UseIntervalOptions::default())
54}
55
56/// Version of [`use_interval`] that takes `UseIntervalOptions`. See [`use_interval`] for how to use.
57pub fn use_interval_with_options<N>(
58    interval: N,
59    options: UseIntervalOptions,
60) -> UseIntervalReturn<
61    impl Fn() + Clone + Send + Sync,
62    impl Fn() + Clone + Send + Sync,
63    impl Fn() + Clone + Send + Sync,
64>
65where
66    N: Into<Signal<u64>>,
67{
68    let UseIntervalOptions {
69        immediate,
70        callback,
71    } = options;
72
73    let (counter, set_counter) = signal(0u64);
74
75    let update = move || set_counter.update(|count| *count += 1);
76    let reset = sendwrap_fn!(move || set_counter.set(0));
77
78    let cb = move || {
79        update();
80        callback(counter.get());
81    };
82
83    let Pausable {
84        is_active,
85        pause,
86        resume,
87    } = use_interval_fn_with_options(
88        cb,
89        interval,
90        UseIntervalFnOptions {
91            immediate,
92            immediate_callback: false,
93        },
94    );
95
96    UseIntervalReturn {
97        counter: counter.into(),
98        reset,
99        is_active,
100        pause,
101        resume,
102    }
103}
104
105/// Options for [`use_interval_with_options`]
106#[derive(DefaultBuilder)]
107pub struct UseIntervalOptions {
108    /// Start the timer immediately. Defaults to `true`.
109    immediate: bool,
110
111    /// Callback on every interval.
112    callback: Rc<dyn Fn(u64)>,
113}
114
115impl Default for UseIntervalOptions {
116    fn default() -> Self {
117        Self {
118            immediate: true,
119            callback: Rc::new(|_: u64| {}),
120        }
121    }
122}
123
124/// Return type of [`use_interval`].
125#[derive(DefaultBuilder)]
126pub struct UseIntervalReturn<PauseFn, ResumeFn, ResetFn>
127where
128    PauseFn: Fn() + Clone + Send + Sync,
129    ResumeFn: Fn() + Clone + Send + Sync,
130    ResetFn: Fn() + Clone + Send + Sync,
131{
132    /// Counter signal that increases by one every interval.
133    pub counter: Signal<u64>,
134
135    /// Reset the counter to zero
136    pub reset: ResetFn,
137
138    /// A Signal that indicates whether the counter is active. `false` when paused.
139    pub is_active: Signal<bool>,
140
141    /// Temporarily pause the counter
142    pub pause: PauseFn,
143
144    /// Resume the counter
145    pub resume: ResumeFn,
146}