dioxus_v04_optional_hooks/
lib.rs

1use std::fmt::Debug;
2use std::future::Future;
3use dioxus::prelude::*;
4
5/// Optional future hook.
6#[derive(Clone)]
7pub struct FutureHook<'a, T, E>
8  where
9    T: 'static + ?Sized + Clone,
10    E: 'static + ?Sized + Clone + Debug,
11{
12  future: &'a UseFuture<Result<T, E>>,
13  outdated_marker: &'a UseState<bool>,
14}
15
16impl<'a, T: ?Sized + Clone, E: ?Sized + Clone + Debug> Copy for FutureHook<'a, T, E> {}
17
18#[derive(PartialEq, Eq)]
19pub enum FutureState {
20  Empty,
21  Ready,
22  Error,
23  Outdated,
24  Reloading,
25}
26
27#[derive(PartialEq, Eq)]
28pub enum StartupGuard {
29  Disable = 0,
30  Enable = 1,
31}
32
33impl<'a, T, E> FutureHook<'a, T, E>
34  where
35    T: 'static + Clone,
36    E: 'static + Clone + Debug,
37{
38  /// Creates new optional future.
39  ///
40  /// Example:
41  /// ```rust,ignore
42  /// use dioxus_v04_optional_hooks::{FutureHook, StartupGuard};
43  /// ...
44  /// let generate_fut = FutureHook::new(cx, StartupGuard::Enable, (dependency_state_hook,) |(dependency_state_hook,)| {
45  ///   async move {
46  ///     some_func(*dependency_state_hook).await
47  ///   }
48  /// });
49  /// ```
50  pub fn new<
51    D: UseFutureDep,
52    F: Future<Output = Result<T, E>> + 'static
53  >(
54    cx: Scope<'a>,
55    startup_guard: StartupGuard,
56    dependencies: D,
57    fut: impl FnOnce(D::Out) -> F
58  ) -> Self {
59    let outdated = startup_guard == StartupGuard::Enable;
60    Self {
61      future: use_future(cx, dependencies, fut),
62      outdated_marker: use_state(cx, || outdated),
63    }
64  }
65
66  /// Extends the standard future states by adding one more.
67  pub fn check_state(&self) -> FutureState {
68    let val = match self.future.state() {
69      UseFutureState::Pending => FutureState::Empty,
70      UseFutureState::Complete(Ok(_)) => FutureState::Ready,
71      UseFutureState::Complete(Err(_)) => FutureState::Error,
72      UseFutureState::Reloading(_) => FutureState::Reloading,
73    };
74    if (val == FutureState::Ready || val == FutureState::Error) && self.is_outdated() {
75      return FutureState::Outdated
76    }
77    val
78  }
79
80  /// Reads the future value, if any.
81  pub fn read(&self, allow_cache_while_reloading: bool) -> Option<&'a T> {
82    if self.is_outdated() { return None }
83    if !allow_cache_while_reloading {
84      match self.check_state() {
85        FutureState::Empty | FutureState::Reloading | FutureState::Error | FutureState::Outdated => { None },
86        FutureState::Ready => {
87          let val = self.future.value().as_ref().unwrap().as_ref().unwrap();
88          Some(val)
89        },
90      }
91    } else {
92      match self.check_state() {
93        FutureState::Empty | FutureState::Error => { None },
94        FutureState::Ready | FutureState::Reloading => {
95          let val_p = self.future.value();
96          let val = val_p.as_ref().unwrap();
97          match val.as_ref() {
98            Err(_) => None,
99            Ok(val) => Some(val),
100          }
101        },
102        FutureState::Outdated => {
103          if let UseFutureState::Complete(Ok(val)) = self.future.state() {
104            Some(val)
105          } else {
106            None
107          }
108        },
109      }
110    }
111  }
112
113  /// Clones the value.
114  pub fn read_clone(&self, allow_cache_while_reloading: bool) -> Option<T> {
115    self.read(allow_cache_while_reloading).cloned()
116  }
117
118  /// Gets the future value directly.
119  pub fn read_unchecked(&self) -> Option<&'a Result<T, E>> {
120    self.future.value()
121  }
122
123  /// Restarts the future hook.
124  pub fn restart(&self) {
125    if self.check_state() == FutureState::Empty || self.check_state() == FutureState::Reloading { return }
126
127    self.outdated_marker.set(false);
128    self.future.restart();
129  }
130
131  /// Restarts the future only if it's outdated.
132  pub fn fetch(&self) {
133    if self.is_outdated() { self.restart(); }
134  }
135  
136  /// Restarts the future only if it's not `Ready`, even if it's marked as outdated.
137  /// 
138  /// Dioxus's mechanism exposed to be state-dependable, and if state is updated, future updates whenever it's fetched or not.
139  /// To avoid the starting of future twice, `lazy_fetch` marks the hook as not outdated if it has the value.
140  pub fn lazy_fetch(&self) {
141    if self.is_outdated() {
142      self.outdated_marker.set(false);
143      self.restart();
144    } else {
145      self.fetch();
146    }
147  }
148
149  /// Checks if the future is outdated.
150  pub fn is_outdated(&self) -> bool {
151    **self.outdated_marker
152  }
153
154  /// Sets the future outdated.
155  pub fn set_outdated(&self) {
156    self.outdated_marker.set(true)
157  }
158
159  /// Clones outdated marker to use into closures.
160  pub fn get_outdated_marker(&self) -> UseState<bool> {
161    self.outdated_marker.to_owned()
162  }
163}