yew_hooks/hooks/
use_timeout.rs

1use std::rc::Rc;
2
3use gloo::timers::callback::Timeout;
4use yew::prelude::*;
5
6use super::{use_mut_latest, use_unmount};
7
8/// State handle for the [`use_timeout`] hook.
9pub struct UseTimeoutHandle {
10    reset: Rc<dyn Fn()>,
11    cancel: Rc<dyn Fn()>,
12}
13
14impl UseTimeoutHandle {
15    /// Reset the timeout.
16    pub fn reset(&self) {
17        (self.reset)();
18    }
19
20    /// Cancel the timeout.
21    pub fn cancel(&self) {
22        (self.cancel)();
23    }
24}
25
26impl Clone for UseTimeoutHandle {
27    fn clone(&self) -> Self {
28        Self {
29            reset: self.reset.clone(),
30            cancel: self.cancel.clone(),
31        }
32    }
33}
34
35/// A hook that schedules a timeout to invoke `callback` in `millis` milliseconds from now.
36/// The timeout will be cancelled if `millis` is set to 0 or `cancel()` is called.
37///
38/// # Example
39///
40/// ```rust
41/// # use yew::prelude::*;
42/// #
43/// use yew_hooks::prelude::*;
44///
45/// #[function_component(Timeout)]
46/// fn timeout() -> Html {
47///     let state = use_state(|| 0);
48///
49///     let timeout = {
50///         let state = state.clone();
51///         use_timeout(move || {
52///             state.set(*state + 1);
53///         }, 2000)
54///     };
55///
56///     let onreset = {
57///         let timeout = timeout.clone();
58///         Callback::from(move |_| timeout.reset())
59///     };
60///
61///     let oncancel = {
62///         let timeout = timeout.clone();
63///         Callback::from(move |_| timeout.cancel())
64///     };
65///     
66///     html! {
67///         <>
68///             <button onclick={onreset}>{ "Reset timeout" }</button>
69///             <button onclick={oncancel}>{ "Cancel timeout" }</button>
70///             { *state }
71///         </>
72///     }
73/// }
74/// ```
75#[hook]
76pub fn use_timeout<Callback>(callback: Callback, millis: u32) -> UseTimeoutHandle
77where
78    Callback: FnOnce() + 'static,
79{
80    let callback_ref = use_mut_latest(Some(callback));
81    let timeout_ref = use_mut_ref(|| None);
82
83    let reset = {
84        let timeout_ref = timeout_ref.clone();
85        Rc::new(move || {
86            let timeout_ref = timeout_ref.clone();
87            let callback_ref = callback_ref.clone();
88            if millis > 0 {
89                *timeout_ref.borrow_mut() = Some(Timeout::new(millis, move || {
90                    let callback_ref = callback_ref.current();
91                    let callback = (*callback_ref.borrow_mut()).take();
92                    if let Some(callback) = callback {
93                        callback();
94                    }
95                }));
96            } else {
97                *timeout_ref.borrow_mut() = None;
98            }
99        })
100    };
101
102    let cancel = {
103        let timeout_ref = timeout_ref.clone();
104        Rc::new(move || {
105            *timeout_ref.borrow_mut() = None;
106        })
107    };
108
109    {
110        let reset = reset.clone();
111        let timeout_ref = timeout_ref.clone();
112        use_effect_with(millis, move |_| {
113            reset();
114
115            move || *timeout_ref.borrow_mut() = None
116        });
117    }
118
119    use_unmount(move || {
120        *timeout_ref.borrow_mut() = None;
121    });
122
123    UseTimeoutHandle { reset, cancel }
124}