use_future.rs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. #![allow(missing_docs)]
  2. use dioxus_core::{ScopeState, TaskId};
  3. use std::{any::Any, cell::Cell, future::Future, rc::Rc, sync::Arc};
  4. use crate::{use_state, UseState};
  5. /// A future that resolves to a value.
  6. ///
  7. /// This runs the future only once - though the future may be regenerated
  8. /// through the [`UseFuture::restart`] method.
  9. ///
  10. /// This is commonly used for components that cannot be rendered until some
  11. /// asynchronous operation has completed.
  12. ///
  13. /// Whenever the hooks dependencies change, the future will be re-evaluated.
  14. /// If a future is pending when the dependencies change, the previous future
  15. /// will be canceled before the new one is started.
  16. ///
  17. /// - dependencies: a tuple of references to values that are PartialEq + Clone
  18. pub fn use_future<T, F, D>(
  19. cx: &ScopeState,
  20. dependencies: D,
  21. future: impl FnOnce(D::Out) -> F,
  22. ) -> &UseFuture<T>
  23. where
  24. T: 'static,
  25. F: Future<Output = T> + 'static,
  26. D: UseFutureDep,
  27. {
  28. let val = use_state(cx, || None);
  29. let state = cx.use_hook(move || UseFuture {
  30. update: cx.schedule_update(),
  31. needs_regen: Rc::new(Cell::new(true)),
  32. state: val.clone(),
  33. task: Default::default(),
  34. });
  35. let state_dependencies = cx.use_hook(Vec::new);
  36. if dependencies.clone().apply(state_dependencies) || state.needs_regen.get() {
  37. // kill the old one, if it exists
  38. if let Some(task) = state.task.take() {
  39. cx.remove_future(task);
  40. }
  41. // Create the new future
  42. let fut = future(dependencies.out());
  43. let val = val.clone();
  44. let task = state.task.clone();
  45. state.task.set(Some(cx.push_future(async move {
  46. val.set(Some(fut.await));
  47. task.take();
  48. })));
  49. // Mark that we don't need to regenerate
  50. state.needs_regen.set(false);
  51. }
  52. // update the current value
  53. state.state.current_val = val.current_val.clone();
  54. state
  55. }
  56. pub enum FutureState<'a, T> {
  57. Pending,
  58. Complete(&'a T),
  59. Regenerating(&'a T), // the old value
  60. }
  61. #[derive(Clone)]
  62. pub struct UseFuture<T: 'static> {
  63. update: Arc<dyn Fn()>,
  64. needs_regen: Rc<Cell<bool>>,
  65. task: Rc<Cell<Option<TaskId>>>,
  66. state: UseState<Option<T>>,
  67. }
  68. pub enum UseFutureState<'a, T> {
  69. Pending,
  70. Complete(&'a T),
  71. Reloading(&'a T),
  72. }
  73. impl<T> UseFuture<T> {
  74. /// Restart the future with new dependencies.
  75. ///
  76. /// Will not cancel the previous future, but will ignore any values that it
  77. /// generates.
  78. pub fn restart(&self) {
  79. self.needs_regen.set(true);
  80. (self.update)();
  81. }
  82. /// Forcefully cancel a future
  83. pub fn cancel(&self, cx: &ScopeState) {
  84. if let Some(task) = self.task.take() {
  85. cx.remove_future(task);
  86. }
  87. }
  88. // Manually set the value in the future slot without starting the future over
  89. pub fn set(&self, new_value: T) {
  90. self.state.set(Some(new_value));
  91. }
  92. /// Return any value, even old values if the future has not yet resolved.
  93. ///
  94. /// If the future has never completed, the returned value will be `None`.
  95. pub fn value(&self) -> Option<&T> {
  96. self.state.current_val.as_ref().as_ref()
  97. }
  98. /// Get the ID of the future in Dioxus' internal scheduler
  99. pub fn task(&self) -> Option<TaskId> {
  100. self.task.get()
  101. }
  102. /// Get the current state of the future.
  103. pub fn state(&self) -> UseFutureState<T> {
  104. match (&self.task.get(), &self.value()) {
  105. // If we have a task and an existing value, we're reloading
  106. (Some(_), Some(val)) => UseFutureState::Reloading(val),
  107. // no task, but value - we're done
  108. (None, Some(val)) => UseFutureState::Complete(val),
  109. // no task, no value - something's wrong? return pending
  110. (None, None) => UseFutureState::Pending,
  111. // Task, no value - we're still pending
  112. (Some(_), None) => UseFutureState::Pending,
  113. }
  114. }
  115. }
  116. pub trait UseFutureDep: Sized + Clone {
  117. type Out;
  118. fn out(&self) -> Self::Out;
  119. fn apply(self, state: &mut Vec<Box<dyn Any>>) -> bool;
  120. }
  121. impl UseFutureDep for () {
  122. type Out = ();
  123. fn out(&self) -> Self::Out {}
  124. fn apply(self, _state: &mut Vec<Box<dyn Any>>) -> bool {
  125. false
  126. }
  127. }
  128. pub trait Dep: 'static + PartialEq + Clone {}
  129. impl<T> Dep for T where T: 'static + PartialEq + Clone {}
  130. impl<A: Dep> UseFutureDep for &A {
  131. type Out = A;
  132. fn out(&self) -> Self::Out {
  133. (*self).clone()
  134. }
  135. fn apply(self, state: &mut Vec<Box<dyn Any>>) -> bool {
  136. match state.get_mut(0).and_then(|f| f.downcast_mut::<A>()) {
  137. Some(val) => {
  138. if *val != *self {
  139. *val = self.clone();
  140. return true;
  141. }
  142. }
  143. None => {
  144. state.push(Box::new(self.clone()));
  145. return true;
  146. }
  147. }
  148. false
  149. }
  150. }
  151. macro_rules! impl_dep {
  152. (
  153. $($el:ident=$name:ident,)*
  154. ) => {
  155. impl< $($el),* > UseFutureDep for ($(&$el,)*)
  156. where
  157. $(
  158. $el: Dep
  159. ),*
  160. {
  161. type Out = ($($el,)*);
  162. fn out(&self) -> Self::Out {
  163. let ($($name,)*) = self;
  164. ($((*$name).clone(),)*)
  165. }
  166. #[allow(unused)]
  167. fn apply(self, state: &mut Vec<Box<dyn Any>>) -> bool {
  168. let ($($name,)*) = self;
  169. let mut idx = 0;
  170. let mut needs_regen = false;
  171. $(
  172. match state.get_mut(idx).map(|f| f.downcast_mut::<$el>()).flatten() {
  173. Some(val) => {
  174. if *val != *$name {
  175. *val = $name.clone();
  176. needs_regen = true;
  177. }
  178. }
  179. None => {
  180. state.push(Box::new($name.clone()));
  181. needs_regen = true;
  182. }
  183. }
  184. idx += 1;
  185. )*
  186. needs_regen
  187. }
  188. }
  189. };
  190. }
  191. impl_dep!(A = a,);
  192. impl_dep!(A = a, B = b,);
  193. impl_dep!(A = a, B = b, C = c,);
  194. impl_dep!(A = a, B = b, C = c, D = d,);
  195. impl_dep!(A = a, B = b, C = c, D = d, E = e,);
  196. impl_dep!(A = a, B = b, C = c, D = d, E = e, F = f,);
  197. impl_dep!(A = a, B = b, C = c, D = d, E = e, F = f, G = g,);
  198. impl_dep!(A = a, B = b, C = c, D = d, E = e, F = f, G = g, H = h,);
  199. /// A helper macro that merges uses the closure syntax to elaborate the dependency array
  200. #[macro_export]
  201. macro_rules! use_future {
  202. ($cx:ident, || $($rest:tt)*) => { use_future( $cx, (), |_| $($rest)* ) };
  203. ($cx:ident, | $($args:tt),* | $($rest:tt)*) => {
  204. use_future(
  205. $cx,
  206. ($($args),*),
  207. |($($args),*)| $($rest)*
  208. )
  209. };
  210. }
  211. #[cfg(test)]
  212. mod tests {
  213. use super::*;
  214. #[allow(unused)]
  215. #[test]
  216. fn test_use_future() {
  217. use dioxus_core::prelude::*;
  218. struct MyProps {
  219. a: String,
  220. b: i32,
  221. c: i32,
  222. d: i32,
  223. e: i32,
  224. }
  225. async fn app(cx: Scope<'_, MyProps>) -> Element {
  226. // should only ever run once
  227. let fut = use_future(cx, (), |_| async move {});
  228. // runs when a is changed
  229. let fut = use_future(cx, (&cx.props.a,), |(a,)| async move {});
  230. // runs when a or b is changed
  231. let fut = use_future(cx, (&cx.props.a, &cx.props.b), |(a, b)| async move { 123 });
  232. let a = use_future!(cx, || async move {
  233. // do the thing!
  234. });
  235. let b = &123;
  236. let c = &123;
  237. let a = use_future!(cx, |b, c| async move {
  238. let a = b + c;
  239. let blah = "asd";
  240. });
  241. todo!()
  242. }
  243. }
  244. }