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