usestate.rs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. use dioxus_core::prelude::Context;
  2. use std::{
  3. cell::{Cell, Ref, RefCell, RefMut},
  4. fmt::Display,
  5. ops::{Deref, DerefMut},
  6. rc::Rc,
  7. };
  8. /// Store state between component renders!
  9. ///
  10. /// ## The "King" of state hooks
  11. ///
  12. /// The Dioxus version of `useState` is the "king daddy" of state management. It allows you to ergonomically store and
  13. /// modify state between component renders. When the state is updated, the component will re-render.
  14. ///
  15. /// Dioxus' use_state basically wraps a RefCell with helper methods and integrates it with the VirtualDOM update system.
  16. ///
  17. /// [`use_state`] exposes a few helper methods to modify the underlying state:
  18. /// - `.set(new)` allows you to override the "work in progress" value with a new value
  19. /// - `.get_mut()` allows you to modify the WIP value
  20. /// - `.get_wip()` allows you to access the WIP value
  21. /// - `.deref()` provides the previous value (often done implicitly, though a manual dereference with `*` might be required)
  22. ///
  23. /// Additionally, a ton of std::ops traits are implemented for the `UseState` wrapper, meaning any mutative type operations
  24. /// will automatically be called on the WIP value.
  25. ///
  26. ///
  27. /// Usage:
  28. /// ```ignore
  29. /// const Example: FC<()> = |cx| {
  30. /// let counter = use_state(cx, || 0);
  31. /// let increment = |_| counter += 1;
  32. /// let decrement = |_| counter += 1;
  33. ///
  34. /// html! {
  35. /// <div>
  36. /// <h1>"Counter: {counter}" </h1>
  37. /// <button onclick={increment}> "Increment" </button>
  38. /// <button onclick={decrement}> "Decrement" </button>
  39. /// </div>
  40. /// }
  41. /// }
  42. /// ```
  43. pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T, P>(
  44. cx: Context<'a, P>,
  45. initial_state_fn: F,
  46. ) -> UseState<T> {
  47. cx.use_hook(
  48. move || UseStateInner {
  49. current_val: initial_state_fn(),
  50. callback: cx.schedule_update(),
  51. wip: RefCell::new(None),
  52. update_scheuled: Cell::new(false),
  53. },
  54. move |hook| {
  55. hook.update_scheuled.set(false);
  56. let mut new_val = hook.wip.borrow_mut();
  57. if new_val.is_some() {
  58. hook.current_val = new_val.take().unwrap();
  59. }
  60. UseState { inner: &*hook }
  61. },
  62. |_| {},
  63. )
  64. }
  65. struct UseStateInner<T: 'static> {
  66. current_val: T,
  67. update_scheuled: Cell<bool>,
  68. callback: Rc<dyn Fn()>,
  69. wip: RefCell<Option<T>>,
  70. updater:
  71. }
  72. pub struct UseState<'a, T: 'static> {
  73. inner: &'a UseStateInner<T>,
  74. }
  75. impl<T> Copy for UseState<'_, T> {}
  76. impl<'a, T> Clone for UseState<'a, T>
  77. where
  78. T: 'static,
  79. {
  80. fn clone(&self) -> Self {
  81. UseState { inner: self.inner }
  82. }
  83. }
  84. impl<'a, T: 'static> UseState<'a, T> {
  85. /// Tell the Dioxus Scheduler that we need to be processed
  86. pub fn needs_update(&self) {
  87. if !self.inner.update_scheuled.get() {
  88. self.inner.update_scheuled.set(true);
  89. (self.inner.callback)();
  90. }
  91. }
  92. pub fn set(&self, new_val: T) {
  93. self.needs_update();
  94. *self.inner.wip.borrow_mut() = Some(new_val);
  95. }
  96. pub fn get(&self) -> &T {
  97. &self.inner.current_val
  98. }
  99. /// Get the current status of the work-in-progress data
  100. pub fn get_wip(&self) -> Ref<Option<T>> {
  101. self.inner.wip.borrow()
  102. }
  103. pub fn classic(self) -> (&'a T, &'a Rc<dyn Fn(T)>) {
  104. (&self.inner.current_val)
  105. }
  106. }
  107. impl<'a, T: 'static + ToOwned<Owned = T>> UseState<'a, T> {
  108. pub fn get_mut(self) -> RefMut<'a, T> {
  109. // make sure we get processed
  110. self.needs_update();
  111. // Bring out the new value, cloning if it we need to
  112. // "get_mut" is locked behind "ToOwned" to make it explicit that cloning occurs to use this
  113. RefMut::map(self.inner.wip.borrow_mut(), |slot| {
  114. if slot.is_none() {
  115. *slot = Some(self.inner.current_val.to_owned());
  116. }
  117. slot.as_mut().unwrap()
  118. })
  119. }
  120. }
  121. impl<'a, T: 'static> std::ops::Deref for UseState<'a, T> {
  122. type Target = T;
  123. fn deref(&self) -> &Self::Target {
  124. &self.inner.current_val
  125. }
  126. }
  127. use std::ops::{Add, AddAssign, Sub, SubAssign};
  128. impl<'a, T: Copy + Add<T, Output = T>> Add<T> for UseState<'a, T> {
  129. type Output = T;
  130. fn add(self, rhs: T) -> Self::Output {
  131. self.inner.current_val.add(rhs)
  132. }
  133. }
  134. impl<'a, T: Copy + Add<T, Output = T>> AddAssign<T> for UseState<'a, T> {
  135. fn add_assign(&mut self, rhs: T) {
  136. self.set(self.inner.current_val.add(rhs));
  137. }
  138. }
  139. impl<'a, T: Copy + Sub<T, Output = T>> Sub<T> for UseState<'a, T> {
  140. type Output = T;
  141. fn sub(self, rhs: T) -> Self::Output {
  142. self.inner.current_val.sub(rhs)
  143. }
  144. }
  145. impl<'a, T: Copy + Sub<T, Output = T>> SubAssign<T> for UseState<'a, T> {
  146. fn sub_assign(&mut self, rhs: T) {
  147. self.set(self.inner.current_val.sub(rhs));
  148. }
  149. }
  150. // enable displaty for the handle
  151. impl<'a, T: 'static + Display> std::fmt::Display for UseState<'a, T> {
  152. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  153. write!(f, "{}", self.inner.current_val)
  154. }
  155. }