yew_hooks/hooks/use_size.rs
1use wasm_bindgen::{prelude::*, JsCast};
2use web_sys::Element;
3use yew::prelude::*;
4
5use super::use_raf_state;
6use crate::web_sys_ext::{ResizeObserver, ResizeObserverEntry};
7
8/// A sensor hook that tracks an HTML element's dimensions using the `ResizeObserver` API.
9///
10/// # Example
11///
12/// ```rust
13/// # use yew::prelude::*;
14/// #
15/// use yew_hooks::prelude::*;
16///
17/// #[function_component(UseSize)]
18/// fn size() -> Html {
19/// let node = use_node_ref();
20/// let state = use_size(node.clone());
21///
22/// html! {
23/// <div ref={node}>
24/// <b>{ " Width: " }</b>
25/// { state.0 }
26/// <b>{ " Height: " }</b>
27/// { state.1 }
28/// </div>
29/// }
30/// }
31/// ```
32#[hook]
33pub fn use_size(node: NodeRef) -> (u32, u32) {
34 let state = use_raf_state(|| (0, 0));
35
36 {
37 let state = state.clone();
38 use_effect_with(node, move |node| {
39 let closure = Closure::wrap(Box::new(move |entries: Vec<ResizeObserverEntry>| {
40 for entry in &entries {
41 let element = entry.target();
42 state.set((
43 element.client_width() as u32,
44 element.client_height() as u32,
45 ));
46 }
47 }) as Box<dyn Fn(Vec<ResizeObserverEntry>)>);
48
49 let observer = ResizeObserver::new(closure.as_ref().unchecked_ref()).unwrap_throw();
50 // Forget the closure to keep it alive
51 closure.forget();
52
53 if let Some(element) = &node.cast::<Element>() {
54 observer.observe(element);
55 }
56
57 move || observer.disconnect()
58 });
59 }
60
61 *state
62}