1
0

hooks.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. //! Useful, foundational hooks that 3rd parties can implement.
  2. //! Currently implemented:
  3. //! - [x] use_ref
  4. //! - [x] use_state
  5. //! - [ ] use_reducer
  6. //! - [ ] use_effect
  7. use crate::innerlude::Context;
  8. use crate::innerlude::*;
  9. use std::{
  10. cell::RefCell,
  11. ops::{Deref, DerefMut},
  12. rc::Rc,
  13. };
  14. /// Store state between component renders!
  15. /// When called, this hook retrives a stored value and provides a setter to update that value.
  16. /// When the setter is called, the component is re-ran with the new value.
  17. ///
  18. /// This is behaves almost exactly the same way as React's "use_state".
  19. ///
  20. /// Usage:
  21. /// ```ignore
  22. /// static Example: FC<()> = |cx| {
  23. /// let (counter, set_counter) = use_state(&cx, || 0);
  24. /// let increment = |_| set_couter(counter + 1);
  25. /// let decrement = |_| set_couter(counter + 1);
  26. ///
  27. /// html! {
  28. /// <div>
  29. /// <h1>"Counter: {counter}" </h1>
  30. /// <button onclick={increment}> "Increment" </button>
  31. /// <button onclick={decrement}> "Decrement" </button>
  32. /// </div>
  33. /// }
  34. /// }
  35. /// ```
  36. pub fn use_state_classic<'a, 'c, T: 'static, F: FnOnce() -> T, P>(
  37. cx: Context<'a, P>,
  38. initial_state_fn: F,
  39. ) -> (&'a T, &'a Rc<dyn Fn(T)>) {
  40. struct UseState<T: 'static> {
  41. new_val: Rc<RefCell<Option<T>>>,
  42. current_val: T,
  43. caller: Rc<dyn Fn(T) + 'static>,
  44. }
  45. cx.use_hook(
  46. move || UseState {
  47. new_val: Rc::new(RefCell::new(None)),
  48. current_val: initial_state_fn(),
  49. caller: Rc::new(|_| println!("setter called!")),
  50. },
  51. move |hook| {
  52. log::debug!("Use_state set called");
  53. let inner = hook.new_val.clone();
  54. let scheduled_update = cx.schedule_update();
  55. // get ownership of the new val and replace the current with the new
  56. // -> as_ref -> borrow_mut -> deref_mut -> take
  57. // -> rc -> &RefCell -> RefMut -> &Option<T> -> T
  58. if let Some(new_val) = hook.new_val.as_ref().borrow_mut().deref_mut().take() {
  59. hook.current_val = new_val;
  60. }
  61. // todo: swap out the caller with a subscription call and an internal update
  62. hook.caller = Rc::new(move |new_val| {
  63. // update the setter with the new value
  64. let mut new_inner = inner.as_ref().borrow_mut();
  65. *new_inner = Some(new_val);
  66. // Ensure the component gets updated
  67. scheduled_update();
  68. });
  69. // box gets derefed into a ref which is then taken as ref with the hook
  70. (&hook.current_val, &hook.caller)
  71. },
  72. |_| {},
  73. )
  74. }
  75. use crate::innerlude::*;
  76. use std::{
  77. fmt::{Debug, Display},
  78. pin::Pin,
  79. };
  80. pub struct UseState<T: 'static> {
  81. modifier: Rc<RefCell<Option<Box<dyn FnOnce(&mut T)>>>>,
  82. current_val: T,
  83. update: Box<dyn Fn() + 'static>,
  84. setter: Box<dyn Fn(T) + 'static>,
  85. // setter: Box<dyn Fn(T) + 'static>,
  86. }
  87. // #[derive(Clone, Copy)]
  88. // pub struct UseStateHandle<'a, T: 'static> {
  89. // inner: &'a UseState<T>,
  90. // // pub setter: &'a dyn Fn(T),
  91. // // pub modifier: &'a dyn Fn(&mut T),
  92. // }
  93. impl<'a, T: 'static> UseState<T> {
  94. pub fn setter(&self) -> &dyn Fn(T) {
  95. &self.setter
  96. // let r = self.setter.as_mut();
  97. // unsafe { Pin::get_unchecked_mut(r) }
  98. }
  99. pub fn set(&self, new_val: T) {
  100. self.modify(|f| *f = new_val);
  101. }
  102. // signal that we need to be updated
  103. // save the modifier
  104. pub fn modify(&self, f: impl FnOnce(&mut T) + 'static) {
  105. let mut slot = self.modifier.as_ref().borrow_mut();
  106. *slot = Some(Box::new(f));
  107. (self.update)();
  108. }
  109. }
  110. impl<'a, T: 'static> std::ops::Deref for UseState<T> {
  111. type Target = T;
  112. fn deref(&self) -> &Self::Target {
  113. &self.current_val
  114. }
  115. }
  116. // enable displaty for the handle
  117. impl<'a, T: 'static + Display> std::fmt::Display for UseState<T> {
  118. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  119. write!(f, "{}", self.current_val)
  120. }
  121. }
  122. /// Store state between component renders!
  123. /// When called, this hook retrives a stored value and provides a setter to update that value.
  124. /// When the setter is called, the component is re-ran with the new value.
  125. ///
  126. /// This is behaves almost exactly the same way as React's "use_state".
  127. ///
  128. /// Usage:
  129. /// ```ignore
  130. /// static Example: FC<()> = |cx| {
  131. /// let (counter, set_counter) = use_state(&cx, || 0);
  132. /// let increment = |_| set_couter(counter + 1);
  133. /// let decrement = |_| set_couter(counter + 1);
  134. ///
  135. /// html! {
  136. /// <div>
  137. /// <h1>"Counter: {counter}" </h1>
  138. /// <button onclick={increment}> "Increment" </button>
  139. /// <button onclick={decrement}> "Decrement" </button>
  140. /// </div>
  141. /// }
  142. /// }
  143. /// ```
  144. pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T, P>(
  145. cx: Context<'a, P>,
  146. initial_state_fn: F,
  147. ) -> &'a UseState<T> {
  148. cx.use_hook(
  149. move || UseState {
  150. modifier: Rc::new(RefCell::new(None)),
  151. current_val: initial_state_fn(),
  152. update: Box::new(|| {}),
  153. setter: Box::new(|_| {}),
  154. },
  155. move |hook| {
  156. log::debug!("addr of hook: {:#?}", hook as *const _);
  157. let scheduled_update = cx.schedule_update();
  158. // log::debug!("Checking if new value {:#?}", &hook.current_val);
  159. // get ownership of the new val and replace the current with the new
  160. // -> as_ref -> borrow_mut -> deref_mut -> take
  161. // -> rc -> &RefCell -> RefMut -> &Option<T> -> T
  162. if let Some(new_val) = hook.modifier.as_ref().borrow_mut().deref_mut().take() {
  163. // log::debug!("setting prev {:#?}", &hook.current_val);
  164. (new_val)(&mut hook.current_val);
  165. // log::debug!("setting new value {:#?}", &hook.current_val);
  166. }
  167. hook.update = Box::new(move || scheduled_update());
  168. let modifier = hook.modifier.clone();
  169. hook.setter = Box::new(move |new_val: T| {
  170. let mut slot = modifier.as_ref().borrow_mut();
  171. let slot2 = slot.deref_mut();
  172. *slot2 = Some(Box::new(move |old: &mut T| *old = new_val));
  173. });
  174. &*hook
  175. },
  176. |_| {},
  177. )
  178. }
  179. // pub struct UseRef<T: 'static> {
  180. // _current: RefCell<T>,
  181. // }
  182. // impl<T: 'static> UseRef<T> {
  183. // fn new(val: T) -> Self {
  184. // Self {
  185. // _current: RefCell::new(val),
  186. // }
  187. // }
  188. // pub fn set(&self, new: T) {
  189. // let mut val = self._current.borrow_mut();
  190. // *val = new;
  191. // }
  192. // pub fn modify(&self, modifier: impl FnOnce(&mut T)) {
  193. // let mut val = self._current.borrow_mut();
  194. // let val_as_ref = val.deref_mut();
  195. // modifier(val_as_ref);
  196. // }
  197. // pub fn current(&self) -> std::cell::Ref<'_, T> {
  198. // self._current.borrow()
  199. // }
  200. // }
  201. /// Store a mutable value between renders!
  202. /// To read the value, borrow the ref.
  203. /// To change it, use modify.
  204. /// Modifications to this value do not cause updates to the component
  205. /// Attach to inner context reference, so context can be consumed
  206. pub fn use_ref<'a, T: 'static, P>(
  207. cx: Context<'a, P>,
  208. initial_state_fn: impl FnOnce() -> T + 'static,
  209. ) -> &'a RefCell<T> {
  210. cx.use_hook(|| RefCell::new(initial_state_fn()), |state| &*state, |_| {})
  211. }
  212. struct UseReducer<T: 'static, R: 'static> {
  213. new_val: Rc<RefCell<Option<T>>>,
  214. current_val: T,
  215. caller: Box<dyn Fn(R) + 'static>,
  216. }
  217. /// Store state between component renders!
  218. /// When called, this hook retrives a stored value and provides a setter to update that value.
  219. /// When the setter is called, the component is re-ran with the new value.
  220. ///
  221. /// This is behaves almost exactly the same way as React's "use_state".
  222. ///
  223. pub fn use_reducer<'a, 'c, State: 'static, Action: 'static, P>(
  224. cx: Context<'a, P>,
  225. initial_state_fn: impl FnOnce() -> State,
  226. _reducer: impl Fn(&mut State, Action),
  227. ) -> (&'a State, &'a Box<dyn Fn(Action)>) {
  228. cx.use_hook(
  229. move || UseReducer {
  230. new_val: Rc::new(RefCell::new(None)),
  231. current_val: initial_state_fn(),
  232. caller: Box::new(|_| println!("setter called!")),
  233. },
  234. move |hook| {
  235. let _inner = hook.new_val.clone();
  236. let scheduled_update = cx.schedule_update();
  237. // get ownership of the new val and replace the current with the new
  238. // -> as_ref -> borrow_mut -> deref_mut -> take
  239. // -> rc -> &RefCell -> RefMut -> &Option<T> -> T
  240. if let Some(new_val) = hook.new_val.as_ref().borrow_mut().deref_mut().take() {
  241. hook.current_val = new_val;
  242. }
  243. // todo: swap out the caller with a subscription call and an internal update
  244. hook.caller = Box::new(move |_new_val| {
  245. // update the setter with the new value
  246. // let mut new_inner = inner.as_ref().borrow_mut();
  247. // *new_inner = Some(new_val);
  248. // Ensure the component gets updated
  249. scheduled_update();
  250. });
  251. // box gets derefed into a ref which is then taken as ref with the hook
  252. (&hook.current_val, &hook.caller)
  253. },
  254. |_| {},
  255. )
  256. }
  257. /// Use model makes it easy to use "models" as state for components. To modify the model, call "modify" and a clone of the
  258. /// current model will be made, with a RefMut lock on it. Dioxus will never run your components multithreaded, so you can
  259. /// be relatively sure that this won't fail in practice
  260. pub fn use_model<'a, T: ToOwned<Owned = T> + 'static, P>(
  261. cx: Context<'a, P>,
  262. f: impl FnOnce() -> T,
  263. ) -> &'a UseModel<T> {
  264. cx.use_hook(
  265. move || {
  266. let real = f();
  267. let wip = RefCell::new(real.to_owned());
  268. let update = cx.schedule_update();
  269. UseModel { real, wip, update }
  270. },
  271. |hook| {
  272. hook.real = hook.wip.borrow().to_owned();
  273. &*hook
  274. },
  275. |_| {},
  276. )
  277. }
  278. pub struct UseModel<T: ToOwned> {
  279. real: T,
  280. wip: RefCell<T>,
  281. update: Rc<dyn Fn()>,
  282. }
  283. use std::cell::{Ref, RefMut};
  284. impl<T: ToOwned> Deref for UseModel<T> {
  285. type Target = T;
  286. fn deref(&self) -> &Self::Target {
  287. &self.real
  288. }
  289. }
  290. impl<T: Display + ToOwned> Display for UseModel<T> {
  291. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  292. write!(f, "{}", self.real)
  293. }
  294. }
  295. impl<T: ToOwned> UseModel<T> {
  296. pub fn get_mut(&self) -> RefMut<'_, T> {
  297. (self.update)();
  298. self.wip.borrow_mut()
  299. }
  300. pub fn modify(&self, f: impl FnOnce(&mut T)) {
  301. (self.update)();
  302. let mut g = self.get_mut();
  303. let r = g.deref_mut();
  304. f(r)
  305. }
  306. }
  307. // #[cfg(test)]
  308. mod tests {
  309. use crate::prelude::*;
  310. enum Actions {
  311. Incr,
  312. Decr,
  313. }
  314. // #[allow(unused)]
  315. // static Example: FC<()> = |cx| {
  316. // let (count, reduce) = use_reducer(
  317. // &cx,
  318. // || 0,
  319. // |count, action| match action {
  320. // Actions::Incr => *count += 1,
  321. // Actions::Decr => *count -= 1,
  322. // },
  323. // );
  324. // cx.render(rsx! {
  325. // div {
  326. // h1 {"Count: {count}"}
  327. // button {
  328. // "Increment"
  329. // onclick: move |_| reduce(Actions::Incr)
  330. // }
  331. // button {
  332. // "Decrement"
  333. // onclick: move |_| reduce(Actions::Decr)
  334. // }
  335. // }
  336. // })
  337. // };
  338. }
  339. pub fn use_is_initialized<'a, P>(cx: Context<'a, P>) -> bool {
  340. let val = use_ref(cx, || false);
  341. match *val.borrow() {
  342. true => true,
  343. false => {
  344. //
  345. *val.borrow_mut() = true;
  346. false
  347. }
  348. }
  349. }