use_effect.rs 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. use dioxus_core::{ScopeState, TaskId};
  2. use std::{any::Any, cell::Cell, future::Future};
  3. use crate::UseFutureDep;
  4. /// A hook that provides a future that executes after the hooks have been applied.
  5. ///
  6. /// Whenever the hooks dependencies change, the future will be re-evaluated.
  7. /// If a future is pending when the dependencies change, the previous future
  8. /// will be allowed to continue.
  9. ///
  10. /// **Note:** If your dependency list is always empty, use [`use_on_create`](crate::use_on_create).
  11. ///
  12. /// ## Arguments
  13. ///
  14. /// - `dependencies`: a tuple of references to values that are `PartialEq` + `Clone`.
  15. /// - `future`: a closure that takes the `dependencies` as arguments and returns a `'static` future.
  16. ///
  17. /// ## Examples
  18. ///
  19. /// ```rust, no_run
  20. /// # use dioxus::prelude::*;
  21. /// #[component]
  22. /// fn Profile(cx: Scope, id: usize) -> Element {
  23. /// let name = use_state(cx, || None);
  24. ///
  25. /// // Only fetch the user data when the id changes.
  26. /// use_effect(cx, (id,), |(id,)| {
  27. /// to_owned![name];
  28. /// async move {
  29. /// let user = fetch_user(id).await;
  30. /// name.set(user.name);
  31. /// }
  32. /// });
  33. ///
  34. /// let name = name.get().clone().unwrap_or("Loading...".to_string());
  35. ///
  36. /// render!(
  37. /// p { "{name}" }
  38. /// )
  39. /// }
  40. ///
  41. /// #[component]
  42. /// fn App(cx: Scope) -> Element {
  43. /// render!(Profile { id: 0 })
  44. /// }
  45. /// ```
  46. pub fn use_effect<T, F, D>(cx: &ScopeState, dependencies: D, future: impl FnOnce(D::Out) -> F)
  47. where
  48. T: 'static,
  49. F: Future<Output = T> + 'static,
  50. D: UseFutureDep,
  51. {
  52. struct UseEffect {
  53. needs_regen: bool,
  54. task: Cell<Option<TaskId>>,
  55. dependencies: Vec<Box<dyn Any>>,
  56. }
  57. let state = cx.use_hook(move || UseEffect {
  58. needs_regen: true,
  59. task: Cell::new(None),
  60. dependencies: Vec::new(),
  61. });
  62. if dependencies.clone().apply(&mut state.dependencies) || state.needs_regen {
  63. // We don't need regen anymore
  64. state.needs_regen = false;
  65. // Create the new future
  66. let fut = future(dependencies.out());
  67. state.task.set(Some(cx.push_future(async move {
  68. fut.await;
  69. })));
  70. }
  71. }
  72. #[cfg(test)]
  73. mod tests {
  74. use super::*;
  75. #[allow(unused)]
  76. #[test]
  77. fn test_use_future() {
  78. use dioxus_core::prelude::*;
  79. struct MyProps {
  80. a: String,
  81. b: i32,
  82. c: i32,
  83. d: i32,
  84. e: i32,
  85. }
  86. fn app(cx: Scope<MyProps>) -> Element {
  87. // should only ever run once
  88. use_effect(cx, (), |_| async move {
  89. //
  90. });
  91. // runs when a is changed
  92. use_effect(cx, (&cx.props.a,), |(a,)| async move {
  93. //
  94. });
  95. // runs when a or b is changed
  96. use_effect(cx, (&cx.props.a, &cx.props.b), |(a, b)| async move {
  97. //
  98. });
  99. todo!()
  100. }
  101. }
  102. }