leptos_use/
use_interval_fn.rs1#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
2
3use crate::sendwrap_fn;
4use crate::utils::Pausable;
5use default_struct_builder::DefaultBuilder;
6use leptos::leptos_dom::helpers::IntervalHandle;
7use leptos::prelude::*;
8use send_wrapper::SendWrapper;
9use std::cell::Cell;
10use std::sync::Arc;
11use std::time::Duration;
12
13pub fn use_interval_fn<CbFn, N>(
47 callback: CbFn,
48 interval: N,
49) -> Pausable<impl Fn() + Clone + Send + Sync, impl Fn() + Clone + Send + Sync>
50where
51 CbFn: Fn() + Clone + 'static,
52 N: Into<Signal<u64>>,
53{
54 use_interval_fn_with_options(callback, interval, UseIntervalFnOptions::default())
55}
56
57pub fn use_interval_fn_with_options<CbFn, N>(
59 callback: CbFn,
60 interval: N,
61 options: UseIntervalFnOptions,
62) -> Pausable<impl Fn() + Clone + Send + Sync, impl Fn() + Clone + Send + Sync>
63where
64 CbFn: Fn() + Clone + 'static,
65 N: Into<Signal<u64>>,
66{
67 let UseIntervalFnOptions {
68 immediate,
69 immediate_callback,
70 } = options;
71
72 let timer: Arc<SendWrapper<Cell<Option<IntervalHandle>>>> =
73 Arc::new(SendWrapper::new(Cell::new(None)));
74
75 let (is_active, set_active) = signal(false);
76
77 let clean = {
78 let timer = Arc::clone(&timer);
79
80 move || {
81 if let Some(handle) = Cell::take(&timer) {
82 handle.clear();
83 }
84 }
85 };
86
87 let pause = {
88 let clean = clean.clone();
89
90 move || {
91 set_active.set(false);
92 clean();
93 }
94 };
95
96 let interval = interval.into();
97
98 let resume = sendwrap_fn!(move || {
99 #[cfg(not(feature = "ssr"))]
100 {
101 let interval_value = interval.get();
102 if interval_value == 0 {
103 return;
104 }
105
106 set_active.set(true);
107
108 let callback = {
109 let callback = callback.clone();
110
111 move || {
112 #[cfg(debug_assertions)]
113 let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
114
115 callback();
116 }
117 };
118
119 if immediate_callback {
120 callback.clone()();
121 }
122 clean();
123
124 timer.set(
125 set_interval_with_handle(callback.clone(), Duration::from_millis(interval_value))
126 .ok(),
127 );
128 }
129 });
130
131 if immediate {
132 resume();
133 }
134
135 {
136 #[allow(clippy::clone_on_copy)]
137 let resume = resume.clone();
138
139 let effect = Effect::watch(
140 move || interval.get(),
141 move |_, _, _| {
142 if is_active.get() {
143 resume();
144 }
145 },
146 false,
147 );
148 on_cleanup(move || effect.stop());
149 }
150
151 on_cleanup({
152 let pause = SendWrapper::new(pause.clone());
153 #[allow(clippy::redundant_closure)]
154 move || pause()
155 });
156
157 Pausable {
158 is_active: is_active.into(),
159 pause,
160 resume,
161 }
162}
163
164#[derive(DefaultBuilder)]
166pub struct UseIntervalFnOptions {
167 pub immediate: bool,
169
170 pub immediate_callback: bool,
172}
173
174impl Default for UseIntervalFnOptions {
175 fn default() -> Self {
176 Self {
177 immediate: true,
178 immediate_callback: false,
179 }
180 }
181}