hooks.rs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. //! Built-in hooks
  2. //!
  3. //! This module contains all the low-level built-in hooks that require first party support to work.
  4. //!
  5. //! Hooks:
  6. //! - [`use_hook`]
  7. //! - [`use_state_provider`]
  8. //! - [`use_state_consumer`]
  9. //! - [`use_task`]
  10. //! - [`use_suspense`]
  11. use crate::innerlude::*;
  12. use futures_util::FutureExt;
  13. use std::{any::Any, cell::RefCell, future::Future, ops::Deref, rc::Rc};
  14. /// Awaits the given task, forcing the component to re-render when the value is ready.
  15. ///
  16. /// Returns the handle to the task and the value (if it is ready, else None).
  17. ///
  18. /// ```
  19. /// static Example: FC<()> = |(cx, props)| {
  20. /// let (task, value) = use_task(|| async {
  21. /// timer::sleep(Duration::from_secs(1)).await;
  22. /// "Hello World"
  23. /// });
  24. ///
  25. /// match contents {
  26. /// Some(contents) => rsx!(cx, div { "{title}" }),
  27. /// None => rsx!(cx, div { "Loading..." }),
  28. /// }
  29. /// };
  30. /// ```
  31. pub fn use_task<'src, Out, Fut, Init>(
  32. cx: Context<'src>,
  33. task_initializer: Init,
  34. ) -> (&'src TaskHandle, &'src Option<Out>)
  35. where
  36. Out: 'static,
  37. Fut: Future<Output = Out> + 'static,
  38. Init: FnOnce() -> Fut + 'src,
  39. {
  40. struct TaskHook<T> {
  41. handle: TaskHandle,
  42. task_dump: Rc<RefCell<Option<T>>>,
  43. value: Option<T>,
  44. }
  45. // whenever the task is complete, save it into th
  46. cx.use_hook(
  47. move |_| {
  48. let task_fut = task_initializer();
  49. let task_dump = Rc::new(RefCell::new(None));
  50. let slot = task_dump.clone();
  51. let updater = cx.schedule_update_any();
  52. let originator = cx.scope.our_arena_idx;
  53. let handle = cx.submit_task(Box::pin(task_fut.then(move |output| async move {
  54. *slot.as_ref().borrow_mut() = Some(output);
  55. updater(originator);
  56. originator
  57. })));
  58. TaskHook {
  59. task_dump,
  60. value: None,
  61. handle,
  62. }
  63. },
  64. |hook| {
  65. if let Some(val) = hook.task_dump.as_ref().borrow_mut().take() {
  66. hook.value = Some(val);
  67. }
  68. (&hook.handle, &hook.value)
  69. },
  70. |_| {},
  71. )
  72. }
  73. /// Asynchronously render new nodes once the given future has completed.
  74. ///
  75. /// # Easda
  76. ///
  77. ///
  78. ///
  79. ///
  80. /// # Example
  81. ///
  82. ///
  83. pub fn use_suspense<'src, Out, Fut, Cb>(
  84. cx: Context<'src>,
  85. task_initializer: impl FnOnce() -> Fut,
  86. user_callback: Cb,
  87. ) -> Element<'src>
  88. where
  89. Fut: Future<Output = Out> + 'static,
  90. Out: 'static,
  91. Cb: for<'a> Fn(SuspendedContext<'a>, &Out) -> Element<'a> + 'static,
  92. {
  93. /*
  94. General strategy:
  95. - Create a slot for the future to dump its output into
  96. - Create a new future feeding off the user's future that feeds the output into that slot
  97. - Submit that future as a task
  98. - Take the task handle id and attach that to our suspended node
  99. - when the hook runs, check if the value exists
  100. - if it does, then we can render the node directly
  101. - if it doesn't, then we render a suspended node along with with the callback and task id
  102. */
  103. cx.use_hook(
  104. move |_| {
  105. let value = Rc::new(RefCell::new(None));
  106. let slot = value.clone();
  107. let originator = cx.scope.our_arena_idx;
  108. let handle = cx.submit_task(Box::pin(task_initializer().then(
  109. move |output| async move {
  110. *slot.borrow_mut() = Some(Box::new(output) as Box<dyn Any>);
  111. originator
  112. },
  113. )));
  114. SuspenseHook { handle, value }
  115. },
  116. move |hook| match hook.value.borrow().as_ref() {
  117. Some(value) => {
  118. let out = value.downcast_ref::<Out>().unwrap();
  119. let sus = SuspendedContext {
  120. inner: Context { scope: cx.scope },
  121. };
  122. user_callback(sus, out)
  123. }
  124. None => {
  125. let value = hook.value.clone();
  126. let id = hook.handle.our_id;
  127. todo!()
  128. // Some(LazyNodes::new(move |f| {
  129. // let bump = f.bump();
  130. // use bumpalo::boxed::Box as BumpBox;
  131. // let f: &mut dyn FnMut(SuspendedContext<'src>) -> Element<'src> =
  132. // bump.alloc(move |sus| {
  133. // let val = value.borrow();
  134. // let out = val
  135. // .as_ref()
  136. // .unwrap()
  137. // .as_ref()
  138. // .downcast_ref::<Out>()
  139. // .unwrap();
  140. // user_callback(sus, out)
  141. // });
  142. // let callback = unsafe { BumpBox::from_raw(f) };
  143. // VNode::Suspended(bump.alloc(VSuspended {
  144. // dom_id: empty_cell(),
  145. // task_id: id,
  146. // callback: RefCell::new(Some(callback)),
  147. // }))
  148. // }))
  149. }
  150. },
  151. |_| {},
  152. )
  153. }
  154. pub(crate) struct SuspenseHook {
  155. pub handle: TaskHandle,
  156. pub value: Rc<RefCell<Option<Box<dyn Any>>>>,
  157. }
  158. pub struct SuspendedContext<'a> {
  159. pub(crate) inner: Context<'a>,
  160. }
  161. impl<'src> SuspendedContext<'src> {
  162. // pub fn render(
  163. pub fn render(
  164. // pub fn render<F: FnOnce(NodeFactory<'src>) -> VNode<'src>>(
  165. self,
  166. lazy_nodes: LazyNodes<'_>,
  167. // lazy_nodes: LazyNodes<'src, '_>,
  168. ) -> Element<'src> {
  169. let bump = &self.inner.scope.frames.wip_frame().bump;
  170. todo!("suspense")
  171. // Some(lazy_nodes.into_vnode(NodeFactory { bump }))
  172. }
  173. }
  174. #[derive(Clone, Copy)]
  175. pub struct NodeRef<'src, T: 'static>(&'src RefCell<Option<T>>);
  176. impl<'a, T> Deref for NodeRef<'a, T> {
  177. type Target = RefCell<Option<T>>;
  178. fn deref(&self) -> &Self::Target {
  179. self.0
  180. }
  181. }
  182. pub fn use_node_ref<T, P>(cx: Context) -> NodeRef<T> {
  183. cx.use_hook(|_| RefCell::new(None), |f| NodeRef { 0: f }, |_| {})
  184. }