yew_hooks/hooks/use_throttle.rs
1use std::rc::Rc;
2
3use yew::prelude::*;
4
5use super::{use_mut_latest, use_timeout};
6
7/// State handle for the [`use_throttle`] hook.
8pub struct UseThrottleHandle {
9 run: Rc<dyn Fn()>,
10 cancel: Rc<dyn Fn()>,
11}
12
13impl UseThrottleHandle {
14 /// Run the throttle.
15 pub fn run(&self) {
16 (self.run)();
17 }
18
19 /// Cancel the throttle.
20 pub fn cancel(&self) {
21 (self.cancel)();
22 }
23}
24
25impl Clone for UseThrottleHandle {
26 fn clone(&self) -> Self {
27 Self {
28 run: self.run.clone(),
29 cancel: self.cancel.clone(),
30 }
31 }
32}
33
34/// A hook that throttles invoking a function, the function is only executed once every `millis`.
35///
36/// # Example
37///
38/// ```rust
39/// # use yew::prelude::*;
40/// #
41/// use yew_hooks::prelude::*;
42///
43/// #[function_component(Throttle)]
44/// fn throttle() -> Html {
45/// let state = use_state(|| 0);
46///
47/// let throttle = {
48/// let state = state.clone();
49/// use_throttle(
50/// move || {
51/// state.set(*state + 1);
52/// },
53/// 2000,
54/// )
55/// };
56///
57/// let onclick = {
58/// let throttle = throttle.clone();
59/// Callback::from(move |_| throttle.run())
60/// };
61///
62/// let oncancel = { Callback::from(move |_| throttle.cancel()) };
63///
64/// html! {
65/// <>
66/// <button {onclick}>{ "Click fast!" }</button>
67/// <button onclick={oncancel}>{ "Cancel throttle" }</button>
68/// <b>{ "State: " }</b> {*state}
69/// </>
70/// }
71/// }
72/// ```
73#[hook]
74pub fn use_throttle<Callback>(callback: Callback, millis: u32) -> UseThrottleHandle
75where
76 Callback: FnMut() + 'static,
77{
78 let throttled = use_mut_ref(|| false);
79 let callback_ref = use_mut_latest(callback);
80 let timeout = {
81 let throttled = throttled.clone();
82 use_timeout(
83 move || {
84 *throttled.borrow_mut() = false;
85 },
86 millis,
87 )
88 };
89
90 let run = {
91 let throttled = throttled.clone();
92 let timeout = timeout.clone();
93 Rc::new(move || {
94 let throttled_value = *throttled.borrow();
95 if !throttled_value {
96 let callback_ref = callback_ref.current();
97 let callback = &mut *callback_ref.borrow_mut();
98 callback();
99 *throttled.borrow_mut() = true;
100 timeout.reset();
101 }
102 })
103 };
104
105 let cancel = {
106 Rc::new(move || {
107 timeout.cancel();
108 *throttled.borrow_mut() = false;
109 })
110 };
111
112 UseThrottleHandle { run, cancel }
113}