sketch.rs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. use bumpalo::Bump;
  2. use dioxus_core as dioxus;
  3. use dioxus_core::{
  4. prelude::{html, Context, Properties, VElement, VNode, FC},
  5. scope::Scope,
  6. };
  7. use std::{
  8. any::Any,
  9. borrow::BorrowMut,
  10. cell::RefCell,
  11. mem::swap,
  12. ops::{Deref, DerefMut},
  13. rc::Rc,
  14. };
  15. use std::{borrow::Borrow, sync::atomic::AtomicUsize};
  16. use typed_arena::Arena;
  17. fn main() {
  18. let mut scope = Scope::new(component);
  19. (0..5).for_each(|f| {
  20. let ctx = scope.create_context();
  21. component(ctx);
  22. });
  23. }
  24. // we need to do something about props and context being borrowed from different sources....
  25. // kinda anooying
  26. /// use_ref creates a new value when the component is created and then borrows that value on every render
  27. fn component(ctx: Context<()>) -> VNode {
  28. (0..10).for_each(|f| {
  29. let r = use_ref(&ctx, move || f);
  30. });
  31. todo!()
  32. }
  33. pub struct UseRef<T: 'static> {
  34. current: RefCell<T>,
  35. }
  36. impl<T: 'static> UseRef<T> {
  37. fn new(val: T) -> Self {
  38. Self {
  39. current: RefCell::new(val),
  40. }
  41. }
  42. fn modify(&self, modifier: impl FnOnce(&mut T)) {
  43. let mut val = self.current.borrow_mut();
  44. let val_as_ref = val.deref_mut();
  45. modifier(val_as_ref);
  46. }
  47. }
  48. /// Store a mutable value between renders!
  49. /// To read the value, borrow the ref.
  50. /// To change it, use modify.
  51. /// Modifications to this value do not cause updates to the component
  52. pub fn use_ref<'a, P, T: 'static>(
  53. ctx: &'a Context<'a, P>,
  54. initial_state_fn: impl FnOnce() -> T + 'static,
  55. ) -> &'a UseRef<T> {
  56. ctx.use_hook(
  57. || UseRef::new(initial_state_fn()),
  58. |state, _| &*state,
  59. |_| {},
  60. )
  61. }
  62. struct UseState<T> {
  63. new_val: Rc<RefCell<Option<T>>>,
  64. current_val: T,
  65. caller: Box<dyn Fn(T) + 'static>,
  66. }
  67. /// Store state between component renders!
  68. /// When called, this hook retrives a stored value and provides a setter to update that value.
  69. /// When the setter is called, the component is re-ran with the new value.
  70. ///
  71. /// This is behaves almost exactly the same way as React's "use_state".
  72. ///
  73. /// Usage:
  74. /// ```ignore
  75. /// static Example: FC<()> = |ctx| {
  76. /// let (counter, set_counter) = use_state(ctx, || 0);
  77. /// let increment = || set_couter(counter + 1);
  78. /// let decrement = || set_couter(counter + 1);
  79. ///
  80. /// html! {
  81. /// <div>
  82. /// <h1>"Counter: {counter}" </h1>
  83. /// <button onclick={increment}> "Increment" </button>
  84. /// <button onclick={decrement}> "Decrement" </button>
  85. /// </div>
  86. /// }
  87. /// }
  88. /// ```
  89. pub fn use_state<'a, P: Properties + 'static, T: 'static, F: FnOnce() -> T + 'static>(
  90. ctx: &'a Context<'a, P>,
  91. initial_state_fn: F,
  92. ) -> (&T, &impl Fn(T)) {
  93. ctx.use_hook(
  94. move || UseState {
  95. new_val: Rc::new(RefCell::new(None)),
  96. current_val: initial_state_fn(),
  97. caller: Box::new(|_| println!("setter called!")),
  98. },
  99. move |hook, updater| {
  100. let inner = hook.new_val.clone();
  101. // todo: swap out the caller with a subscription call and an internal update
  102. hook.caller = Box::new(move |new_val| {
  103. // update the setter with the new value
  104. let mut new_inner = inner.as_ref().borrow_mut();
  105. *new_inner = Some(new_val);
  106. });
  107. // box gets derefed into a ref which is then taken as ref with the hook
  108. (&hook.current_val, &hook.caller)
  109. },
  110. |_| {},
  111. )
  112. }
  113. fn test_use_state(ctx: Context<()>) -> VNode {
  114. let (val, set_val) = use_state(&ctx, || 10);
  115. // gloriousness!
  116. // closures are taken with ref to ctx :)
  117. // Can freely use hooks
  118. let handler_0: _ = || set_val(val + 1);
  119. let handler_1: _ = || set_val(val + 10);
  120. let handler_2: _ = || set_val(val + 100);
  121. // these fns are captured, boxed into the bump arena, and then attached to the listeners
  122. // the vnodes share the lifetime of these closures (and the hook data)
  123. // whenever a listener wakes up, we take the reference directly from the bump arena and, with a small bit
  124. // of unsafe code, execute the associated closure / listener function
  125. // Those vnodes are then tossed out and new ones are installed, meaning and old references (potentially bad)
  126. // are removed and UB is prevented from /affecting/ the program
  127. {
  128. VNode::text("blah")
  129. }
  130. }
  131. #[allow(unused, non_upper_case_globals)]
  132. static UseStateBorrowedFc: FC<()> = |ctx| {
  133. let (val, set_val) = use_state(&ctx, || 0);
  134. let incr = || set_val(val + 1);
  135. let decr = || set_val(val - 1);
  136. ctx.view(html! {
  137. <div>
  138. <nav class="menu">
  139. <button> "Increment" </button>
  140. <button> "Decrement" </button>
  141. </nav>
  142. <p> "Current value: {val}" </p>
  143. </div>
  144. })
  145. };
  146. /// A useful component
  147. /// With some great documentation
  148. /// it practically speaks for itself.
  149. /// It's also only a server component :o
  150. fn use_state_borrowed(ctx: Context<()>) -> VNode {
  151. let (val, set_val) = use_state(&ctx, || 0);
  152. let incr = || set_val(val + 1);
  153. let decr = || set_val(val - 1);
  154. ctx.view(html! {
  155. <div>
  156. <nav class="menu">
  157. <button> "Increment" </button>
  158. <button> "Decrement" </button>
  159. </nav>
  160. <p> "Current value: {val}" </p>
  161. </div>
  162. })
  163. }
  164. // <button onclick=incr> { "Increment" } </button>
  165. // <button onclick=decr> { "Decrement" } </button>