sketch.rs 4.8 KB

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