yew_hooks/hooks/
use_raf_state.rs

1use std::ops::Deref;
2use std::{cell::RefCell, rc::Rc};
3
4use gloo::render::{request_animation_frame, AnimationFrame};
5use yew::prelude::*;
6
7use super::use_unmount;
8
9/// State handle for the [`use_raf_state`] hook.
10pub struct UseRafStateHandle<T> {
11    inner: UseStateHandle<T>,
12    raf: Rc<RefCell<Option<AnimationFrame>>>,
13}
14
15impl<T> UseRafStateHandle<T>
16where
17    T: 'static,
18{
19    /// Replaces the value.
20    pub fn set(&self, value: T) {
21        let inner = self.inner.clone();
22        *self.raf.borrow_mut() = Some(request_animation_frame(move |_| {
23            inner.set(value);
24        }));
25    }
26}
27
28impl<T> Deref for UseRafStateHandle<T> {
29    type Target = T;
30
31    fn deref(&self) -> &Self::Target {
32        &self.inner
33    }
34}
35
36impl<T> Clone for UseRafStateHandle<T> {
37    fn clone(&self) -> Self {
38        Self {
39            inner: self.inner.clone(),
40            raf: self.raf.clone(),
41        }
42    }
43}
44
45impl<T> PartialEq for UseRafStateHandle<T>
46where
47    T: PartialEq,
48{
49    fn eq(&self, other: &Self) -> bool {
50        *self.inner == *other.inner
51    }
52}
53
54/// A state hook that only updates state in the callback of `requestAnimationFrame`.
55///
56/// # Example
57///
58/// ```rust
59/// # use web_sys::Window;
60/// # use yew::prelude::*;
61/// #
62/// use yew_hooks::prelude::*;
63///
64/// #[function_component(UseRafState)]
65/// fn raf_state() -> Html {
66///     let state = use_raf_state(|| (0f64, 0f64));
67///
68///     {
69///         let state = state.clone();
70///         use_event_with_window("resize", move |e: Event| {
71///             let window: Window = e.target_unchecked_into();
72///             state.set((
73///                 window.inner_width().unwrap().as_f64().unwrap(),
74///                 window.inner_height().unwrap().as_f64().unwrap(),
75///             ));
76///         });
77///     }
78///     
79///     html! {
80///         <>
81///             <b>{ " Width: " }</b>
82///             { state.0 }
83///             <b>{ " Height: " }</b>
84///             { state.1 }
85///         </>
86///     }
87/// }
88/// ```
89#[hook]
90pub fn use_raf_state<T, F>(init_fn: F) -> UseRafStateHandle<T>
91where
92    T: 'static,
93    F: FnOnce() -> T,
94{
95    let inner = use_state(init_fn);
96    let raf = use_mut_ref(|| None);
97
98    {
99        let raf = raf.clone();
100        use_unmount(move || {
101            *raf.borrow_mut() = None;
102        });
103    }
104
105    UseRafStateHandle { inner, raf }
106}