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"))
}