1
0

useref.rs 7.1 KB

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