use_ref.rs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. use dioxus_core::ScopeState;
  2. use std::{
  3. cell::{Cell, Ref, RefCell, RefMut},
  4. rc::Rc,
  5. sync::Arc,
  6. };
  7. /// `use_ref` is a key foundational hook for storing state in Dioxus.
  8. ///
  9. /// It is different that `use_state` in that the value stored is not "immutable".
  10. /// Instead, UseRef is designed to store larger values that will be mutated at will.
  11. ///
  12. /// ## Writing Values
  13. ///
  14. /// Generally, `use_ref` is just a wrapper around a RefCell that tracks mutable
  15. /// writes through the `write` method. Whenever `write` is called, the component
  16. /// that initialized the hook will be marked as "dirty".
  17. ///
  18. /// ```rust, no_run
  19. /// let val = use_ref(|| HashMap::<u32, String>::new());
  20. ///
  21. /// // using `write` will give us a `RefMut` to the inner value, which we can call methods on
  22. /// // This marks the component as "dirty"
  23. /// val.write().insert(1, "hello".to_string());
  24. /// ```
  25. ///
  26. /// You can avoid this default behavior with `write_silent`
  27. ///
  28. /// ```rust, no_run
  29. /// // with `write_silent`, the component will not be re-rendered
  30. /// val.write_silent().insert(2, "goodbye".to_string());
  31. /// ```
  32. ///
  33. /// ## Reading Values
  34. ///
  35. /// To read values out of the refcell, you can use the `read` method which will retrun a `Ref`.
  36. ///
  37. /// ```rust, no_run
  38. /// let map: Ref<_> = val.read();
  39. ///
  40. /// let item = map.get(&1);
  41. /// ```
  42. ///
  43. /// To get an &T out of the RefCell, you need to "reborrow" through the Ref:
  44. ///
  45. /// ```rust, no_run
  46. /// let read = val.read();
  47. /// let map = &*read;
  48. /// ```
  49. ///
  50. /// ## Collections and iteration
  51. ///
  52. /// A common usecase for `use_ref` is to store a large amount of data in a component.
  53. /// Typically this will be a collection like a HashMap or a Vec. To create new
  54. /// elements from the collection, we can use `read()` directly in our rsx!.
  55. ///
  56. /// ```rust, no_run
  57. /// rsx!{
  58. /// val.read().iter().map(|(k, v)| {
  59. /// rsx!{ key: "{k}", "{v}" }
  60. /// })
  61. /// }
  62. /// ```
  63. ///
  64. /// If you are generating elements outside of `rsx!` then you might need to call
  65. /// "render" inside the iterator. For some cases you might need to collect into
  66. /// a temporary Vec.
  67. ///
  68. /// ```rust, no_run
  69. /// let items = val.read().iter().map(|(k, v)| {
  70. /// cx.render(rsx!{ key: "{k}", "{v}" })
  71. /// });
  72. ///
  73. /// // collect into a Vec
  74. ///
  75. /// let items: Vec<Element> = items.collect();
  76. /// ```
  77. ///
  78. /// ## Use in Async
  79. ///
  80. /// To access values from a `UseRef` in an async context, you need to detach it
  81. /// from the current scope's lifetime, making it a `'static` value. This is done
  82. /// by simply calling `to_owned` or `clone`.
  83. ///
  84. /// ```rust, no_run
  85. /// let val = use_ref(|| HashMap::<u32, String>::new());
  86. ///
  87. /// cx.spawn({
  88. /// let val = val.clone();
  89. /// async move {
  90. /// some_work().await;
  91. /// val.write().insert(1, "hello".to_string());
  92. /// }
  93. /// })
  94. /// ```
  95. ///
  96. /// If you're working with lots of values like UseState and UseRef, you can use the
  97. /// `to_owned!` macro to make it easier to write the above code.
  98. ///
  99. /// ```rust, no_run
  100. /// let val1 = use_ref(|| HashMap::<u32, String>::new());
  101. /// let val2 = use_ref(|| HashMap::<u32, String>::new());
  102. /// let val3 = use_ref(|| HashMap::<u32, String>::new());
  103. ///
  104. /// cx.spawn({
  105. /// to_owned![val1, val2, val3];
  106. /// async move {
  107. /// some_work().await;
  108. /// val.write().insert(1, "hello".to_string());
  109. /// }
  110. /// })
  111. /// ```
  112. pub fn use_ref<T: 'static>(cx: &ScopeState, initialize_refcell: impl FnOnce() -> T) -> &UseRef<T> {
  113. let hook = cx.use_hook(|| UseRef {
  114. update: cx.schedule_update(),
  115. value: Rc::new(RefCell::new(initialize_refcell())),
  116. dirty: Rc::new(Cell::new(false)),
  117. gen: 0,
  118. });
  119. if hook.dirty.get() {
  120. hook.gen += 1;
  121. hook.dirty.set(false);
  122. }
  123. hook
  124. }
  125. /// A type created by the [`use_ref`] hook. See its documentation for more details.
  126. pub struct UseRef<T> {
  127. update: Arc<dyn Fn()>,
  128. value: Rc<RefCell<T>>,
  129. dirty: Rc<Cell<bool>>,
  130. gen: usize,
  131. }
  132. impl<T> Clone for UseRef<T> {
  133. fn clone(&self) -> Self {
  134. Self {
  135. update: self.update.clone(),
  136. value: self.value.clone(),
  137. dirty: self.dirty.clone(),
  138. gen: self.gen,
  139. }
  140. }
  141. }
  142. impl<T> UseRef<T> {
  143. /// Read the value in the RefCell into a `Ref`. If this method is called
  144. /// while other values are still being `read` or `write`, then your app will crash.
  145. ///
  146. /// Be very careful when working with this method. If you can, consider using
  147. /// the `with` and `with_mut` methods instead, choosing to render Elements
  148. /// during the read calls.
  149. pub fn read(&self) -> Ref<'_, T> {
  150. self.value.borrow()
  151. }
  152. /// Mutably unlock the value in the RefCell. This will mark the component as "dirty"
  153. ///
  154. /// Uses to `write` should be as short as possible.
  155. ///
  156. /// Be very careful when working with this method. If you can, consider using
  157. /// the `with` and `with_mut` methods instead, choosing to render Elements
  158. /// during the read and write calls.
  159. pub fn write(&self) -> RefMut<'_, T> {
  160. self.needs_update();
  161. self.value.borrow_mut()
  162. }
  163. /// Set the curernt value to `new_value`. This will mark the component as "dirty"
  164. ///
  165. /// This change will propagate immediately, so any other contexts that are
  166. /// using this RefCell will also be affected. If called during an async context,
  167. /// the component will not be re-rendered until the next `.await` call.
  168. pub fn set(&self, new: T) {
  169. *self.value.borrow_mut() = new;
  170. self.needs_update();
  171. }
  172. /// Mutably unlock the value in the RefCell. This will not mark the component as dirty.
  173. /// This is useful if you want to do some work without causing the component to re-render.
  174. ///
  175. /// Uses to `write` should be as short as possible.
  176. ///
  177. /// Be very careful when working with this method. If you can, consider using
  178. /// the `with` and `with_mut` methods instead, choosing to render Elements
  179. pub fn write_silent(&self) -> RefMut<'_, T> {
  180. self.value.borrow_mut()
  181. }
  182. /// Take a reference to the inner value termporarily and produce a new value
  183. ///
  184. /// Note: You can always "reborrow" the value through the RefCell.
  185. /// This method just does it for you automatically.
  186. ///
  187. /// ```rust, no_run
  188. /// let val = use_ref(|| HashMap::<u32, String>::new());
  189. ///
  190. ///
  191. /// // use reborrowing
  192. /// let inner = &*val.read();
  193. ///
  194. /// // or, be safer and use `with`
  195. /// val.with(|i| println!("{:?}", i));
  196. /// ```
  197. pub fn with<O>(&self, immutable_callback: impl FnOnce(&T) -> O) -> O {
  198. immutable_callback(&*self.read())
  199. }
  200. /// Take a reference to the inner value termporarily and produce a new value,
  201. /// modifying the original in place.
  202. ///
  203. /// Note: You can always "reborrow" the value through the RefCell.
  204. /// This method just does it for you automatically.
  205. ///
  206. /// ```rust, no_run
  207. /// let val = use_ref(|| HashMap::<u32, String>::new());
  208. ///
  209. ///
  210. /// // use reborrowing
  211. /// let inner = &mut *val.write();
  212. ///
  213. /// // or, be safer and use `with`
  214. /// val.with_mut(|i| i.insert(1, "hi"));
  215. /// ```
  216. pub fn with_mut<O>(&self, mutable_callback: impl FnOnce(&mut T) -> O) -> O {
  217. mutable_callback(&mut *self.write())
  218. }
  219. /// Call the inner callback to mark the originator component as dirty.
  220. ///
  221. /// This will cause the component to be re-rendered after the current scope
  222. /// has ended or the current async task has been yielded through await.
  223. pub fn needs_update(&self) {
  224. self.dirty.set(true);
  225. (self.update)();
  226. }
  227. }
  228. // UseRef memoizes not on value but on cell
  229. // Memoizes on "generation" - so it will cause a re-render if the value changes
  230. impl<T> PartialEq for UseRef<T> {
  231. fn eq(&self, other: &Self) -> bool {
  232. if Rc::ptr_eq(&self.value, &other.value) {
  233. self.gen == other.gen
  234. } else {
  235. false
  236. }
  237. }
  238. }