This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.

yew/functional/hooks/
use_ref.rs

1use std::cell::RefCell;
2use std::rc::Rc;
3
4use crate::functional::{hook, use_state, Hook, HookContext};
5use crate::NodeRef;
6
7struct UseRef<F> {
8    init_fn: F,
9}
10
11impl<T: 'static, F: FnOnce() -> T> Hook for UseRef<F> {
12    type Output = Rc<T>;
13
14    fn run(self, ctx: &mut HookContext) -> Self::Output {
15        ctx.next_state(|_| (self.init_fn)())
16    }
17}
18
19/// This hook is used for obtaining a reference to a stateful value.
20/// Its state persists across renders.
21///
22/// Mutation must be done via interior mutability, such as `Cell` or `RefCell`.
23///
24/// It is important to note that you do not get notified of state changes.
25/// If you need the component to be re-rendered on state change, consider using
26/// [`use_state`](super::use_state()).
27///
28/// # Example
29/// ```rust
30/// use std::cell::Cell;
31/// use std::ops::{Deref, DerefMut};
32/// use std::rc::Rc;
33///
34/// use web_sys::HtmlInputElement;
35/// use yew::prelude::*;
36///
37/// #[function_component(UseRef)]
38/// fn ref_hook() -> Html {
39///     let message = use_state(|| "".to_string());
40///     let message_count = use_ref(|| Cell::new(0));
41///
42///     let onclick = Callback::from(move |e| {
43///         let window = gloo::utils::window();
44///
45///         if message_count.get() > 3 {
46///             window.alert_with_message("Message limit reached");
47///         } else {
48///             message_count.set(message_count.get() + 1);
49///             window.alert_with_message("Message sent");
50///         }
51///     });
52///
53///     let onchange = {
54///         let message = message.clone();
55///         Callback::from(move |e: Event| {
56///             let input: HtmlInputElement = e.target_unchecked_into();
57///             message.set(input.value())
58///         })
59///     };
60///
61///     html! {
62///         <div>
63///             <input {onchange} value={(*message).clone()} />
64///             <button {onclick}>{ "Send" }</button>
65///         </div>
66///     }
67/// }
68pub fn use_ref<T: 'static, F>(init_fn: F) -> impl Hook<Output = Rc<T>>
69where
70    F: FnOnce() -> T,
71{
72    UseRef { init_fn }
73}
74
75/// This hook is used for obtaining a mutable reference to a stateful value.
76/// Its state persists across renders.
77///
78/// It is important to note that you do not get notified of state changes.
79/// If you need the component to be re-rendered on state change, consider using
80/// [`use_state`](super::use_state()).
81///
82/// # Example
83/// ```rust
84/// use std::cell::RefCell;
85/// use std::ops::{Deref, DerefMut};
86/// use std::rc::Rc;
87///
88/// use web_sys::HtmlInputElement;
89/// use yew::prelude::*;
90///
91/// #[function_component(UseRef)]
92/// fn ref_hook() -> Html {
93///     let message = use_state(|| "".to_string());
94///     let message_count = use_mut_ref(|| 0);
95///
96///     let onclick = Callback::from(move |e| {
97///         let window = gloo::utils::window();
98///
99///         if *message_count.borrow_mut() > 3 {
100///             window.alert_with_message("Message limit reached");
101///         } else {
102///             *message_count.borrow_mut() += 1;
103///             window.alert_with_message("Message sent");
104///         }
105///     });
106///
107///     let onchange = {
108///         let message = message.clone();
109///         Callback::from(move |e: Event| {
110///             let input: HtmlInputElement = e.target_unchecked_into();
111///             message.set(input.value())
112///         })
113///     };
114///
115///     html! {
116///         <div>
117///             <input {onchange} value={(*message).clone()} />
118///             <button {onclick}>{ "Send" }</button>
119///         </div>
120///     }
121/// }
122/// ```
123pub fn use_mut_ref<T: 'static, F>(init_fn: F) -> impl Hook<Output = Rc<RefCell<T>>>
124where
125    F: FnOnce() -> T,
126{
127    UseRef {
128        init_fn: || RefCell::new(init_fn()),
129    }
130}
131
132/// This hook is used for obtaining a [`NodeRef`].
133/// It persists across renders.
134///
135/// The `ref` attribute can be used to attach the [`NodeRef`] to an HTML element. In callbacks,
136/// you can then get the DOM `Element` that the `ref` is attached to.
137///
138/// # Example
139///
140/// ```rust
141/// use wasm_bindgen::prelude::Closure;
142/// use wasm_bindgen::JsCast;
143/// use web_sys::{Event, HtmlElement};
144/// use yew::{function_component, html, use_effect_with, use_node_ref, Html};
145///
146/// #[function_component(UseNodeRef)]
147/// pub fn node_ref_hook() -> Html {
148///     let div_ref = use_node_ref();
149///
150///     {
151///         let div_ref = div_ref.clone();
152///
153///         use_effect_with(div_ref, |div_ref| {
154///             let div = div_ref
155///                 .cast::<HtmlElement>()
156///                 .expect("div_ref not attached to div element");
157///
158///             let listener = Closure::<dyn Fn(Event)>::wrap(Box::new(|_| {
159///                 web_sys::console::log_1(&"Clicked!".into());
160///             }));
161///
162///             div.add_event_listener_with_callback("click", listener.as_ref().unchecked_ref())
163///                 .unwrap();
164///
165///             move || {
166///                 div.remove_event_listener_with_callback(
167///                     "click",
168///                     listener.as_ref().unchecked_ref(),
169///                 )
170///                 .unwrap();
171///             }
172///         });
173///     }
174///
175///     html! {
176///         <div ref={div_ref}>
177///             { "Click me and watch the console log!" }
178///         </div>
179///     }
180/// }
181/// ```
182///
183/// # Tip
184///
185/// When conditionally rendering elements you can use `NodeRef` in conjunction with
186/// `use_effect_with` to perform actions each time an element is rendered and just before the
187/// component where the hook is used in is going to be removed from the DOM.
188#[hook]
189pub fn use_node_ref() -> NodeRef {
190    (*use_state(NodeRef::default)).clone()
191}