wasm_react/hooks/
use_js_ref.rs

1use crate::react_bindings;
2use js_sys::Reflect;
3use std::{fmt::Debug, marker::PhantomData};
4use wasm_bindgen::{intern, JsCast, JsValue, UnwrapThrowExt};
5
6/// Allows access to the underlying JS data persisted with [`use_js_ref()`].
7pub struct JsRefContainer<T>(JsValue, PhantomData<T>);
8
9impl<T: JsCast> JsRefContainer<T> {
10  /// Returns the underlying typed JS data.
11  pub fn current(&self) -> Option<T> {
12    self.current_untyped().dyn_into::<T>().ok()
13  }
14
15  /// Returns the underlying JS data as [`JsValue`].
16  pub fn current_untyped(&self) -> JsValue {
17    Reflect::get(&self.0, &intern("current").into())
18      .expect_throw("cannot read from ref container")
19  }
20
21  /// Sets the underlying JS data.
22  pub fn set_current(&self, value: Option<&T>) {
23    Reflect::set(
24      &self.0,
25      &intern("current").into(),
26      value.map(|t| t.as_ref()).unwrap_or(&JsValue::null()),
27    )
28    .expect_throw("cannot write into ref container");
29  }
30}
31
32impl<T> Debug for JsRefContainer<T> {
33  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34    f.debug_tuple("JsRefContainer").field(&self.0).finish()
35  }
36}
37
38impl<T> Clone for JsRefContainer<T> {
39  fn clone(&self) -> Self {
40    Self(self.0.clone(), PhantomData)
41  }
42}
43
44impl<T> AsRef<JsValue> for JsRefContainer<T> {
45  fn as_ref(&self) -> &JsValue {
46    &self.0
47  }
48}
49
50impl<T> From<JsRefContainer<T>> for JsValue {
51  fn from(value: JsRefContainer<T>) -> Self {
52    value.0
53  }
54}
55
56impl<T> From<JsValue> for JsRefContainer<T> {
57  fn from(value: JsValue) -> Self {
58    Self(value, PhantomData)
59  }
60}
61
62/// This hook can persist JS data through the entire lifetime of the component.
63///
64/// Use this if you need JS to set the ref value. If you only need to mutate the
65/// data from Rust, use [`use_ref()`](crate::hooks::use_ref()) instead.
66///
67/// # Example
68///
69/// ```
70/// # use wasm_react::{*, hooks::*};
71/// # struct MyComponent;
72/// impl Component for MyComponent {
73///   fn render(&self) -> VNode {
74///     let input_element = use_js_ref(None);
75///
76///     h!(div)
77///       .build(
78///         h!(input)
79///           .ref_container(&input_element)
80///           .html_type("text")
81///           .build(())
82///       )
83///   }
84/// }
85/// ```
86pub fn use_js_ref<T: JsCast>(init: Option<T>) -> JsRefContainer<T> {
87  let ref_container = react_bindings::use_ref(
88    &init.map(|init| init.into()).unwrap_or(JsValue::null()),
89  );
90
91  JsRefContainer(ref_container, PhantomData)
92}