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}