wasm_react/hooks/
use_ref.rs

1use crate::react_bindings;
2use std::{
3  any::Any,
4  cell::{Ref, RefCell, RefMut},
5  fmt::Debug,
6  rc::Rc,
7};
8use wasm_bindgen::prelude::*;
9
10#[doc(hidden)]
11#[wasm_bindgen(js_name = __WasmReact_RefContainerValue)]
12#[derive(Debug, Clone)]
13pub struct RefContainerValue(pub(crate) Rc<dyn Any>);
14
15impl RefContainerValue {
16  pub fn value<T: 'static>(&self) -> Result<Rc<T>, Rc<dyn Any>> {
17    Rc::downcast::<T>(self.0.clone())
18  }
19}
20
21/// Allows access to the underlying data persisted with [`use_ref()`].
22///
23/// # Panics
24///
25/// The rules of borrowing will be enforced at runtime through a [`RefCell`],
26/// therefore the methods [`RefContainer::current()`],
27/// [`RefContainer::current_mut()`], and [`RefContainer::set_current()`] may
28/// panic accordingly.
29#[derive(Debug)]
30pub struct RefContainer<T>(Rc<RefCell<T>>);
31
32impl<T: 'static> RefContainer<T> {
33  /// Returns a reference to the underlying data.
34  ///
35  /// # Panics
36  ///
37  /// Panics if the underlying data is currently mutably borrowed.
38  pub fn current(&self) -> Ref<'_, T> {
39    self.0.borrow()
40  }
41
42  /// Returns a mutable reference to the underlying data.
43  ///
44  /// # Panics
45  ///
46  /// Panics if the underlying data is currently borrowed.
47  pub fn current_mut(&mut self) -> RefMut<'_, T> {
48    self.0.borrow_mut()
49  }
50
51  /// Sets the underlying data to the given value.
52  ///
53  /// # Panics
54  ///
55  /// Panics if the underlying data is currently borrowed.
56  pub fn set_current(&mut self, value: T) {
57    *self.current_mut() = value;
58  }
59}
60
61impl<T> Clone for RefContainer<T> {
62  fn clone(&self) -> Self {
63    Self(self.0.clone())
64  }
65}
66
67/// This is the main hook for persisting Rust data through the entire lifetime
68/// of the component.
69///
70/// Whenever the component is unmounted by React, the data will also be dropped.
71/// Keep in mind that the inner value of [`use_ref()`] can only be accessed in
72/// Rust. If you need a ref to hold a DOM element (or a JS value in general),
73/// use [`use_js_ref()`](crate::hooks::use_js_ref()) instead.
74///
75/// The component will not rerender when you mutate the underlying data. If you
76/// want that, use [`use_state()`](crate::hooks::use_state()) instead.
77///
78/// # Example
79///
80/// ```
81/// # use wasm_react::{*, hooks::*};
82/// # struct MyData { value: &'static str };
83/// # struct MyComponent { value: &'static str };
84/// #
85/// impl Component for MyComponent {
86///   fn render(&self) -> VNode {
87///     let ref_container = use_ref(MyData {
88///       value: "Hello World!"
89///     });
90///
91///     use_effect({
92///       clones!(self.value, mut ref_container);
93///
94///       move || {
95///         ref_container.current_mut().value = value;
96///       }
97///     }, Deps::some(self.value));
98///
99///     let vnode = h!(div).build(
100///       ref_container.current().value
101///     );
102///     vnode
103///   }
104/// }
105/// ```
106pub fn use_ref<T: 'static>(init: T) -> RefContainer<T> {
107  let mut value = None;
108
109  react_bindings::use_rust_ref(
110    Closure::once(move || RefContainerValue(Rc::new(RefCell::new(init))))
111      .as_ref(),
112    &mut |ref_container_value| {
113      value = Some(
114        ref_container_value
115          .value::<RefCell<T>>()
116          .expect_throw("mismatched ref container type"),
117      );
118    },
119  );
120
121  RefContainer(value.expect_throw("callback was not called"))
122}