use_memo.rs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. use crate::dependency::Dependency;
  2. use crate::use_signal;
  3. use dioxus_core::prelude::*;
  4. use dioxus_signals::{ReactiveContext, ReadOnlySignal, Readable, Signal, SignalData};
  5. use dioxus_signals::{Storage, Writable};
  6. /// Creates a new unsync Selector. The selector will be run immediately and whenever any signal it reads changes.
  7. ///
  8. /// Selectors can be used to efficiently compute derived data from signals.
  9. ///
  10. /// ```rust
  11. /// use dioxus::prelude::*;
  12. /// use dioxus_signals::*;
  13. ///
  14. /// fn App() -> Element {
  15. /// let mut count = use_signal(|| 0);
  16. /// let double = use_memo(move || count * 2);
  17. /// count += 1;
  18. /// assert_eq!(double.value(), count * 2);
  19. ///
  20. /// rsx! { "{double}" }
  21. /// }
  22. /// ```
  23. #[track_caller]
  24. pub fn use_memo<R: PartialEq>(f: impl FnMut() -> R + 'static) -> ReadOnlySignal<R> {
  25. use_maybe_sync_memo(f)
  26. }
  27. /// Creates a new Selector that may be sync. The selector will be run immediately and whenever any signal it reads changes.
  28. ///
  29. /// Selectors can be used to efficiently compute derived data from signals.
  30. ///
  31. /// ```rust
  32. /// use dioxus::prelude::*;
  33. /// use dioxus_signals::*;
  34. ///
  35. /// fn App() -> Element {
  36. /// let mut count = use_signal(cx, || 0);
  37. /// let double = use_memo(cx, move || count * 2);
  38. /// count += 1;
  39. /// assert_eq!(double.value(), count * 2);
  40. ///
  41. /// render! { "{double}" }
  42. /// }
  43. /// ```
  44. #[track_caller]
  45. pub fn use_maybe_sync_memo<R: PartialEq, S: Storage<SignalData<R>>>(
  46. mut f: impl FnMut() -> R + 'static,
  47. ) -> ReadOnlySignal<R, S> {
  48. use_hook(|| {
  49. // Get the current reactive context
  50. let rc = ReactiveContext::new(None);
  51. // Create a new signal in that context, wiring up its dependencies and subscribers
  52. let mut state: Signal<R, S> = rc.run_in(|| Signal::new_maybe_sync(f()));
  53. spawn(async move {
  54. loop {
  55. // Wait for the dom the be finished with sync work
  56. flush_sync().await;
  57. rc.changed().await;
  58. let new = rc.run_in(&mut f);
  59. if new != *state.peek() {
  60. *state.write() = new;
  61. }
  62. }
  63. });
  64. // And just return the readonly variant of that signal
  65. ReadOnlySignal::new_maybe_sync(state)
  66. })
  67. }
  68. /// Creates a new unsync Selector with some local dependencies. The selector will be run immediately and whenever any signal it reads or any dependencies it tracks changes
  69. ///
  70. /// Selectors can be used to efficiently compute derived data from signals.
  71. ///
  72. /// ```rust
  73. /// use dioxus::prelude::*;
  74. ///
  75. /// fn App() -> Element {
  76. /// let mut local_state = use_state(|| 0);
  77. /// let double = use_memo_with_dependencies(cx, (local_state.get(),), move |(local_state,)| local_state * 2);
  78. /// local_state.set(1);
  79. ///
  80. /// render! { "{double}" }
  81. /// }
  82. /// ```
  83. #[track_caller]
  84. pub fn use_memo_with_dependencies<R: PartialEq, D: Dependency>(
  85. dependencies: D,
  86. f: impl FnMut(D::Out) -> R + 'static,
  87. ) -> ReadOnlySignal<R>
  88. where
  89. D::Out: 'static,
  90. {
  91. use_maybe_sync_selector_with_dependencies(dependencies, f)
  92. }
  93. /// Creates a new Selector that may be sync with some local dependencies. The selector will be run immediately and whenever any signal it reads or any dependencies it tracks changes
  94. ///
  95. /// Selectors can be used to efficiently compute derived data from signals.
  96. ///
  97. /// ```rust
  98. /// use dioxus::prelude::*;
  99. /// use dioxus_signals::*;
  100. ///
  101. /// fn App() -> Element {
  102. /// let mut local_state = use_state(|| 0);
  103. /// let double = use_memo_with_dependencies(cx, (local_state.get(),), move |(local_state,)| local_state * 2);
  104. /// local_state.set(1);
  105. ///
  106. /// render! { "{double}" }
  107. /// }
  108. /// ```
  109. #[track_caller]
  110. pub fn use_maybe_sync_selector_with_dependencies<
  111. R: PartialEq,
  112. D: Dependency,
  113. S: Storage<SignalData<R>>,
  114. >(
  115. dependencies: D,
  116. mut f: impl FnMut(D::Out) -> R + 'static,
  117. ) -> ReadOnlySignal<R, S>
  118. where
  119. D::Out: 'static,
  120. {
  121. let mut dependencies_signal = use_signal(|| dependencies.out());
  122. let selector = use_hook(|| {
  123. // Get the current reactive context
  124. let rc = ReactiveContext::new(None);
  125. // Create a new signal in that context, wiring up its dependencies and subscribers
  126. let mut state: Signal<R, S> =
  127. rc.run_in(|| Signal::new_maybe_sync(f(dependencies_signal.read().clone())));
  128. spawn(async move {
  129. loop {
  130. // Wait for the dom the be finished with sync work
  131. flush_sync().await;
  132. rc.changed().await;
  133. let new = rc.run_in(|| f(dependencies_signal.read().clone()));
  134. if new != *state.peek() {
  135. *state.write() = new;
  136. }
  137. }
  138. });
  139. // And just return the readonly variant of that signal
  140. ReadOnlySignal::new_maybe_sync(state)
  141. });
  142. // This will cause a re-run of the selector if the dependencies change
  143. let changed = { dependencies.changed(&*dependencies_signal.read()) };
  144. if changed {
  145. dependencies_signal.set(dependencies.out());
  146. }
  147. selector
  148. }