use_memo.rs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. use crate::dependency::Dependency;
  2. use crate::{use_callback, use_signal};
  3. use dioxus_core::prelude::*;
  4. use dioxus_signals::Memo;
  5. use dioxus_signals::{ReactiveContext, ReadOnlySignal, Readable, Signal, SignalData};
  6. use dioxus_signals::{Storage, Writable};
  7. use futures_util::StreamExt;
  8. /// Creates a new unsync Selector. The selector will be run immediately and whenever any signal it reads changes.
  9. ///
  10. /// Selectors can be used to efficiently compute derived data from signals.
  11. ///
  12. /// ```rust
  13. /// use dioxus::prelude::*;
  14. /// use dioxus_signals::*;
  15. ///
  16. /// fn App() -> Element {
  17. /// let mut count = use_signal(|| 0);
  18. /// let double = use_memo(move || count * 2);
  19. /// count += 1;
  20. /// assert_eq!(double(), count * 2);
  21. ///
  22. /// rsx! { "{double}" }
  23. /// }
  24. /// ```
  25. #[track_caller]
  26. pub fn use_memo<R: PartialEq>(f: impl FnMut() -> R + 'static) -> Memo<R> {
  27. let callback = use_callback(f);
  28. #[allow(clippy::redundant_closure)]
  29. use_hook(|| Signal::memo(move || callback()))
  30. }
  31. /// 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
  32. ///
  33. /// Selectors can be used to efficiently compute derived data from signals.
  34. ///
  35. /// ```rust
  36. /// use dioxus::prelude::*;
  37. ///
  38. /// fn App() -> Element {
  39. /// let mut local_state = use_signal(|| 0);
  40. /// let double = use_memo_with_dependencies((&local_state(),), move |(local_state,)| local_state * 2);
  41. /// local_state.set(1);
  42. ///
  43. /// rsx! { "{double}" }
  44. /// }
  45. /// ```
  46. #[track_caller]
  47. pub fn use_memo_with_dependencies<R: PartialEq, D: Dependency>(
  48. dependencies: D,
  49. f: impl FnMut(D::Out) -> R + 'static,
  50. ) -> ReadOnlySignal<R>
  51. where
  52. D::Out: 'static,
  53. {
  54. use_maybe_sync_memo_with_dependencies(dependencies, f)
  55. }
  56. /// 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
  57. ///
  58. /// Selectors can be used to efficiently compute derived data from signals.
  59. ///
  60. /// ```rust
  61. /// use dioxus::prelude::*;
  62. /// use dioxus_signals::*;
  63. ///
  64. /// fn App() -> Element {
  65. /// let mut local_state = use_signal(|| 0i32);
  66. /// let double: ReadOnlySignal<i32, SyncStorage> = use_maybe_sync_memo_with_dependencies((&local_state(),), move |(local_state,)| local_state * 2);
  67. /// local_state.set(1);
  68. ///
  69. /// rsx! { "{double}" }
  70. /// }
  71. /// ```
  72. #[track_caller]
  73. pub fn use_maybe_sync_memo_with_dependencies<
  74. R: PartialEq,
  75. D: Dependency,
  76. S: Storage<SignalData<R>>,
  77. >(
  78. dependencies: D,
  79. mut f: impl FnMut(D::Out) -> R + 'static,
  80. ) -> ReadOnlySignal<R, S>
  81. where
  82. D::Out: 'static,
  83. {
  84. let mut dependencies_signal = use_signal(|| dependencies.out());
  85. let selector = use_hook(|| {
  86. // Get the current reactive context
  87. let (rc, mut changed) = ReactiveContext::new();
  88. // Create a new signal in that context, wiring up its dependencies and subscribers
  89. let mut state: Signal<R, S> =
  90. rc.run_in(|| Signal::new_maybe_sync(f(dependencies_signal.read().clone())));
  91. spawn(async move {
  92. loop {
  93. // Wait for context to change
  94. let _ = changed.next().await;
  95. let new = rc.run_in(|| f(dependencies_signal.read().clone()));
  96. if new != *state.peek() {
  97. *state.write() = new;
  98. }
  99. }
  100. });
  101. // And just return the readonly variant of that signal
  102. ReadOnlySignal::new_maybe_sync(state)
  103. });
  104. // This will cause a re-run of the selector if the dependencies change
  105. let changed = { dependencies.changed(&*dependencies_signal.read()) };
  106. if changed {
  107. dependencies_signal.set(dependencies.out());
  108. }
  109. selector
  110. }