selector.rs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. use dioxus_core::prelude::*;
  2. use generational_box::Storage;
  3. use crate::dependency::Dependency;
  4. use crate::{get_effect_ref, signal::SignalData, CopyValue, Effect, ReadOnlySignal, Signal};
  5. use crate::{use_signal, EffectInner, EFFECT_STACK};
  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_selector(move || count * 2);
  17. /// count += 1;
  18. /// assert_eq!(double.value(), count * 2);
  19. ///
  20. /// rsx! { "{double}" }
  21. /// }
  22. /// ```
  23. #[track_caller]
  24. #[must_use = "Consider using `use_effect` to rerun a callback when dependencies change"]
  25. pub fn use_selector<R: PartialEq>(f: impl FnMut() -> R + 'static) -> ReadOnlySignal<R> {
  26. use_maybe_sync_selector(f)
  27. }
  28. /// Creates a new Selector that may be sync. The selector will be run immediately and whenever any signal it reads changes.
  29. ///
  30. /// Selectors can be used to efficiently compute derived data from signals.
  31. ///
  32. /// ```rust
  33. /// use dioxus::prelude::*;
  34. /// use dioxus_signals::*;
  35. ///
  36. /// fn App(cx: Scope) -> Element {
  37. /// let mut count = use_signal(cx, || 0);
  38. /// let double = use_selector(cx, move || count * 2);
  39. /// count += 1;
  40. /// assert_eq!(double.value(), count * 2);
  41. ///
  42. /// render! { "{double}" }
  43. /// }
  44. /// ```
  45. #[track_caller]
  46. #[must_use = "Consider using `use_effect` to rerun a callback when dependencies change"]
  47. pub fn use_maybe_sync_selector<R: PartialEq, S: Storage<SignalData<R>>>(
  48. f: impl FnMut() -> R + 'static,
  49. ) -> ReadOnlySignal<R, S> {
  50. use_hook(|| maybe_sync_selector(f))
  51. }
  52. /// 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
  53. ///
  54. /// Selectors can be used to efficiently compute derived data from signals.
  55. ///
  56. /// ```rust
  57. /// use dioxus::prelude::*;
  58. /// use dioxus_signals::*;
  59. ///
  60. /// fn App(cx: Scope) -> Element {
  61. /// let mut local_state = use_state(cx, || 0);
  62. /// let double = use_selector_with_dependencies(cx, (local_state.get(),), move |(local_state,)| local_state * 2);
  63. /// local_state.set(1);
  64. ///
  65. /// render! { "{double}" }
  66. /// }
  67. /// ```
  68. #[track_caller]
  69. #[must_use = "Consider using `use_effect` to rerun a callback when dependencies change"]
  70. pub fn use_selector_with_dependencies<R: PartialEq, D: Dependency>(
  71. dependencies: D,
  72. f: impl FnMut(D::Out) -> R + 'static,
  73. ) -> ReadOnlySignal<R>
  74. where
  75. D::Out: 'static,
  76. {
  77. use_maybe_sync_selector_with_dependencies(dependencies, f)
  78. }
  79. /// 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
  80. ///
  81. /// Selectors can be used to efficiently compute derived data from signals.
  82. ///
  83. /// ```rust
  84. /// use dioxus::prelude::*;
  85. /// use dioxus_signals::*;
  86. ///
  87. /// fn App(cx: Scope) -> Element {
  88. /// let mut local_state = use_state(cx, || 0);
  89. /// let double = use_selector_with_dependencies(cx, (local_state.get(),), move |(local_state,)| local_state * 2);
  90. /// local_state.set(1);
  91. ///
  92. /// render! { "{double}" }
  93. /// }
  94. /// ```
  95. #[track_caller]
  96. #[must_use = "Consider using `use_effect` to rerun a callback when dependencies change"]
  97. pub fn use_maybe_sync_selector_with_dependencies<
  98. R: PartialEq,
  99. D: Dependency,
  100. S: Storage<SignalData<R>>,
  101. >(
  102. dependencies: D,
  103. mut f: impl FnMut(D::Out) -> R + 'static,
  104. ) -> ReadOnlySignal<R, S>
  105. where
  106. D::Out: 'static,
  107. {
  108. let mut dependencies_signal = use_signal(|| dependencies.out());
  109. let selector = use_hook(|| {
  110. maybe_sync_selector(move || {
  111. let deref = &*dependencies_signal.read();
  112. f(deref.clone())
  113. })
  114. });
  115. let changed = { dependencies.changed(&*dependencies_signal.read()) };
  116. if changed {
  117. dependencies_signal.set(dependencies.out());
  118. }
  119. selector
  120. }
  121. /// Creates a new unsync Selector. The selector will be run immediately and whenever any signal it reads changes.
  122. ///
  123. /// Selectors can be used to efficiently compute derived data from signals.
  124. #[track_caller]
  125. pub fn selector<R: PartialEq>(f: impl FnMut() -> R + 'static) -> ReadOnlySignal<R> {
  126. maybe_sync_selector(f)
  127. }
  128. /// Creates a new Selector that may be Sync + Send. The selector will be run immediately and whenever any signal it reads changes.
  129. ///
  130. /// Selectors can be used to efficiently compute derived data from signals.
  131. #[track_caller]
  132. pub fn maybe_sync_selector<R: PartialEq, S: Storage<SignalData<R>>>(
  133. mut f: impl FnMut() -> R + 'static,
  134. ) -> ReadOnlySignal<R, S> {
  135. let mut state = Signal::<R, S> {
  136. inner: CopyValue::invalid(),
  137. };
  138. let effect = Effect {
  139. source: current_scope_id().expect("in a virtual dom"),
  140. inner: CopyValue::invalid(),
  141. };
  142. {
  143. EFFECT_STACK.with(|stack| stack.effects.write().push(effect));
  144. }
  145. state.inner.value.set(SignalData {
  146. subscribers: Default::default(),
  147. update_any: schedule_update_any(),
  148. value: f(),
  149. effect_ref: get_effect_ref(),
  150. });
  151. {
  152. EFFECT_STACK.with(|stack| stack.effects.write().pop());
  153. }
  154. let invalid_id = effect.id();
  155. tracing::trace!("Creating effect: {:?}", invalid_id);
  156. effect.inner.value.set(EffectInner {
  157. callback: Box::new(move || {
  158. let value = f();
  159. let changed = {
  160. let old = state.inner.read();
  161. value != old.value
  162. };
  163. if changed {
  164. state.set(value)
  165. }
  166. }),
  167. id: invalid_id,
  168. });
  169. {
  170. EFFECT_STACK.with(|stack| stack.effect_mapping.write().insert(invalid_id, effect));
  171. }
  172. ReadOnlySignal::new_maybe_sync(state)
  173. }