use_future.rs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. #![allow(missing_docs)]
  2. use crate::{use_callback, use_hook_did_run, use_signal};
  3. use dioxus_core::prelude::*;
  4. use dioxus_signals::*;
  5. use std::future::Future;
  6. use std::ops::Deref;
  7. /// A hook that allows you to spawn a future the first time you render a component.
  8. ///
  9. ///
  10. /// This future will **not** run on the server. To run a future on the server, you should use [`spawn_isomorphic`] directly.
  11. ///
  12. ///
  13. /// `use_future` **won't return a value**. If you want to return a value from a future, use [`crate::use_resource()`] instead.
  14. ///
  15. /// ## Example
  16. ///
  17. /// ```rust
  18. /// # use dioxus::prelude::*;
  19. /// # use std::time::Duration;
  20. /// fn app() -> Element {
  21. /// let mut count = use_signal(|| 0);
  22. /// let mut running = use_signal(|| true);
  23. /// // `use_future` will spawn an infinitely running future that can be started and stopped
  24. /// use_future(move || async move {
  25. /// loop {
  26. /// if running() {
  27. /// count += 1;
  28. /// }
  29. /// tokio::time::sleep(Duration::from_millis(400)).await;
  30. /// }
  31. /// });
  32. /// rsx! {
  33. /// div {
  34. /// h1 { "Current count: {count}" }
  35. /// button { onclick: move |_| running.toggle(), "Start/Stop the count"}
  36. /// button { onclick: move |_| count.set(0), "Reset the count" }
  37. /// }
  38. /// }
  39. /// }
  40. /// ```
  41. #[doc = include_str!("../docs/rules_of_hooks.md")]
  42. #[doc = include_str!("../docs/moving_state_around.md")]
  43. #[doc(alias = "use_async")]
  44. pub fn use_future<F>(mut future: impl FnMut() -> F + 'static) -> UseFuture
  45. where
  46. F: Future + 'static,
  47. {
  48. let mut state = use_signal(|| UseFutureState::Pending);
  49. let callback = use_callback(move |_| {
  50. let fut = future();
  51. spawn(async move {
  52. state.set(UseFutureState::Pending);
  53. fut.await;
  54. state.set(UseFutureState::Ready);
  55. })
  56. });
  57. // Create the task inside a CopyValue so we can reset it in-place later
  58. let task = use_hook(|| CopyValue::new(callback(())));
  59. // Early returns in dioxus have consequences for use_memo, use_resource, and use_future, etc
  60. // We *don't* want futures to be running if the component early returns. It's a rather weird behavior to have
  61. // use_memo running in the background even if the component isn't hitting those hooks anymore.
  62. //
  63. // React solves this by simply not having early returns interleave with hooks.
  64. // However, since dioxus allows early returns (since we use them for suspense), we need to solve this problem
  65. use_hook_did_run(move |did_run| match did_run {
  66. true => task.peek().resume(),
  67. false => task.peek().pause(),
  68. });
  69. UseFuture {
  70. task,
  71. state,
  72. callback,
  73. }
  74. }
  75. #[derive(Clone, Copy, PartialEq)]
  76. pub struct UseFuture {
  77. task: CopyValue<Task>,
  78. state: Signal<UseFutureState>,
  79. callback: Callback<(), Task>,
  80. }
  81. /// A signal that represents the state of a future
  82. // we might add more states (panicked, etc)
  83. #[derive(Clone, Copy, PartialEq, Hash, Eq, Debug)]
  84. pub enum UseFutureState {
  85. /// The future is still running
  86. Pending,
  87. /// The future has been forcefully stopped
  88. Stopped,
  89. /// The future has been paused, tempoarily
  90. Paused,
  91. /// The future has completed
  92. Ready,
  93. }
  94. impl UseFuture {
  95. /// Restart the future with new dependencies.
  96. ///
  97. /// Will not cancel the previous future, but will ignore any values that it
  98. /// generates.
  99. pub fn restart(&mut self) {
  100. self.task.write().cancel();
  101. let new_task = self.callback.call(());
  102. self.task.set(new_task);
  103. }
  104. /// Forcefully cancel a future
  105. pub fn cancel(&mut self) {
  106. self.state.set(UseFutureState::Stopped);
  107. self.task.write().cancel();
  108. }
  109. /// Pause the future
  110. pub fn pause(&mut self) {
  111. self.state.set(UseFutureState::Paused);
  112. self.task.write().pause();
  113. }
  114. /// Resume the future
  115. pub fn resume(&mut self) {
  116. if self.finished() {
  117. return;
  118. }
  119. self.state.set(UseFutureState::Pending);
  120. self.task.write().resume();
  121. }
  122. /// Get a handle to the inner task backing this future
  123. /// Modify the task through this handle will cause inconsistent state
  124. pub fn task(&self) -> Task {
  125. self.task.cloned()
  126. }
  127. /// Is the future currently finished running?
  128. ///
  129. /// Reading this does not subscribe to the future's state
  130. pub fn finished(&self) -> bool {
  131. matches!(
  132. *self.state.peek(),
  133. UseFutureState::Ready | UseFutureState::Stopped
  134. )
  135. }
  136. /// Get the current state of the future.
  137. pub fn state(&self) -> ReadOnlySignal<UseFutureState> {
  138. self.state.into()
  139. }
  140. }
  141. impl From<UseFuture> for ReadOnlySignal<UseFutureState> {
  142. fn from(val: UseFuture) -> Self {
  143. val.state.into()
  144. }
  145. }
  146. impl Readable for UseFuture {
  147. type Target = UseFutureState;
  148. type Storage = UnsyncStorage;
  149. #[track_caller]
  150. fn try_read_unchecked(
  151. &self,
  152. ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
  153. self.state.try_read_unchecked()
  154. }
  155. #[track_caller]
  156. fn try_peek_unchecked(
  157. &self,
  158. ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
  159. self.state.try_peek_unchecked()
  160. }
  161. }
  162. /// Allow calling a signal with signal() syntax
  163. ///
  164. /// Currently only limited to copy types, though could probably specialize for string/arc/rc
  165. impl Deref for UseFuture {
  166. type Target = dyn Fn() -> UseFutureState;
  167. fn deref(&self) -> &Self::Target {
  168. unsafe { Readable::deref_impl(self) }
  169. }
  170. }