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