signal.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. use crate::Memo;
  2. use crate::{
  3. read::Readable, write::Writable, CopyValue, GlobalMemo, GlobalSignal, ReactiveContext,
  4. ReadableRef,
  5. };
  6. use dioxus_core::{prelude::IntoAttributeValue, ScopeId};
  7. use generational_box::{AnyStorage, Storage, SyncStorage, UnsyncStorage};
  8. use std::{
  9. any::Any,
  10. collections::HashSet,
  11. ops::{Deref, DerefMut},
  12. sync::Mutex,
  13. };
  14. /// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
  15. ///
  16. /// ```rust
  17. /// use dioxus::prelude::*;
  18. /// use dioxus_signals::*;
  19. ///
  20. /// #[component]
  21. /// fn App() -> Element {
  22. /// let mut count = use_signal(|| 0);
  23. ///
  24. /// // Because signals have automatic dependency tracking, if you never read them in a component, that component will not be re-rended when the signal is updated.
  25. /// // The app component will never be rerendered in this example.
  26. /// rsx! { Child { state: count } }
  27. /// }
  28. ///
  29. /// #[component]
  30. /// fn Child(mut state: Signal<u32>) -> Element {
  31. /// use_future(move || async move {
  32. /// // Because the signal is a Copy type, we can use it in an async block without cloning it.
  33. /// state += 1;
  34. /// });
  35. ///
  36. /// rsx! {
  37. /// button {
  38. /// onclick: move |_| state += 1,
  39. /// "{state}"
  40. /// }
  41. /// }
  42. /// }
  43. /// ```
  44. pub struct Signal<T: 'static, S: Storage<SignalData<T>> = UnsyncStorage> {
  45. pub(crate) inner: CopyValue<SignalData<T>, S>,
  46. }
  47. /// A signal that can safely shared between threads.
  48. pub type SyncSignal<T> = Signal<T, SyncStorage>;
  49. /// The data stored for tracking in a signal.
  50. pub struct SignalData<T> {
  51. pub(crate) subscribers: Mutex<HashSet<ReactiveContext>>,
  52. pub(crate) value: T,
  53. }
  54. impl<T: 'static> Signal<T> {
  55. /// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
  56. #[track_caller]
  57. pub fn new(value: T) -> Self {
  58. Self::new_maybe_sync(value)
  59. }
  60. /// Create a new signal with a custom owner scope. The signal will be dropped when the owner scope is dropped instead of the current scope.
  61. #[track_caller]
  62. pub fn new_in_scope(value: T, owner: ScopeId) -> Self {
  63. Self::new_maybe_sync_in_scope(value, owner)
  64. }
  65. /// Creates a new global Signal that can be used in a global static.
  66. #[track_caller]
  67. pub const fn global(constructor: fn() -> T) -> GlobalSignal<T> {
  68. GlobalSignal::new(constructor)
  69. }
  70. }
  71. impl<T: PartialEq + 'static> Signal<T> {
  72. /// Creates a new global Signal that can be used in a global static.
  73. #[track_caller]
  74. pub const fn global_memo(constructor: fn() -> T) -> GlobalMemo<T> {
  75. GlobalMemo::new(constructor)
  76. }
  77. /// Creates a new unsync Selector. The selector will be run immediately and whenever any signal it reads changes.
  78. ///
  79. /// Selectors can be used to efficiently compute derived data from signals.
  80. #[track_caller]
  81. pub fn memo(f: impl FnMut() -> T + 'static) -> Memo<T> {
  82. Memo::new(f)
  83. }
  84. }
  85. impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
  86. /// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
  87. #[track_caller]
  88. #[tracing::instrument(skip(value))]
  89. pub fn new_maybe_sync(value: T) -> Self {
  90. Self {
  91. inner: CopyValue::<SignalData<T>, S>::new_maybe_sync(SignalData {
  92. subscribers: Default::default(),
  93. value,
  94. }),
  95. }
  96. }
  97. /// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
  98. pub fn new_with_caller(
  99. value: T,
  100. #[cfg(debug_assertions)] caller: &'static std::panic::Location<'static>,
  101. ) -> Self {
  102. Self {
  103. inner: CopyValue::new_with_caller(
  104. SignalData {
  105. subscribers: Default::default(),
  106. value,
  107. },
  108. #[cfg(debug_assertions)]
  109. caller,
  110. ),
  111. }
  112. }
  113. /// Create a new signal with a custom owner scope. The signal will be dropped when the owner scope is dropped instead of the current scope.
  114. #[track_caller]
  115. #[tracing::instrument(skip(value))]
  116. pub fn new_maybe_sync_in_scope(value: T, owner: ScopeId) -> Self {
  117. Self {
  118. inner: CopyValue::<SignalData<T>, S>::new_maybe_sync_in_scope(
  119. SignalData {
  120. subscribers: Default::default(),
  121. value,
  122. },
  123. owner,
  124. ),
  125. }
  126. }
  127. /// Take the value out of the signal, invalidating the signal in the process.
  128. pub fn take(&self) -> T {
  129. self.inner.take().value
  130. }
  131. /// Get the scope the signal was created in.
  132. pub fn origin_scope(&self) -> ScopeId {
  133. self.inner.origin_scope()
  134. }
  135. fn update_subscribers(&self) {
  136. {
  137. let inner = self.inner.read();
  138. let mut subscribers = inner.subscribers.lock().unwrap();
  139. subscribers.retain(|reactive_context| reactive_context.mark_dirty())
  140. }
  141. }
  142. /// Get the generational id of the signal.
  143. pub fn id(&self) -> generational_box::GenerationalBoxId {
  144. self.inner.id()
  145. }
  146. }
  147. impl<T, S: Storage<SignalData<T>>> Readable for Signal<T, S> {
  148. type Target = T;
  149. type Storage = S;
  150. #[track_caller]
  151. fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> {
  152. let inner = self.inner.try_read()?;
  153. if let Some(reactive_context) = ReactiveContext::current() {
  154. tracing::trace!("Subscribing to the reactive context {}", reactive_context);
  155. inner.subscribers.lock().unwrap().insert(reactive_context);
  156. }
  157. Ok(S::map(inner, |v| &v.value))
  158. }
  159. /// Get the current value of the signal. **Unlike read, this will not subscribe the current scope to the signal which can cause parts of your UI to not update.**
  160. ///
  161. /// If the signal has been dropped, this will panic.
  162. fn peek(&self) -> ReadableRef<Self> {
  163. let inner = self.inner.read();
  164. S::map(inner, |v| &v.value)
  165. }
  166. }
  167. impl<T: 'static, S: Storage<SignalData<T>>> Writable for Signal<T, S> {
  168. type Mut<R: ?Sized + 'static> = Write<R, S>;
  169. fn map_mut<I: ?Sized, U: ?Sized + 'static, F: FnOnce(&mut I) -> &mut U>(
  170. ref_: Self::Mut<I>,
  171. f: F,
  172. ) -> Self::Mut<U> {
  173. Write::map(ref_, f)
  174. }
  175. fn try_map_mut<
  176. I: ?Sized + 'static,
  177. U: ?Sized + 'static,
  178. F: FnOnce(&mut I) -> Option<&mut U>,
  179. >(
  180. ref_: Self::Mut<I>,
  181. f: F,
  182. ) -> Option<Self::Mut<U>> {
  183. Write::filter_map(ref_, f)
  184. }
  185. #[track_caller]
  186. fn try_write(&mut self) -> Result<Self::Mut<T>, generational_box::BorrowMutError> {
  187. self.inner.try_write().map(|inner| {
  188. let borrow = S::map_mut(inner, |v| &mut v.value);
  189. Write {
  190. write: borrow,
  191. drop_signal: Box::new(SignalSubscriberDrop {
  192. signal: *self,
  193. #[cfg(debug_assertions)]
  194. origin: std::panic::Location::caller(),
  195. }),
  196. }
  197. })
  198. }
  199. }
  200. impl<T> IntoAttributeValue for Signal<T>
  201. where
  202. T: Clone + IntoAttributeValue,
  203. {
  204. fn into_value(self) -> dioxus_core::AttributeValue {
  205. self.with(|f| f.clone().into_value())
  206. }
  207. }
  208. impl<T: 'static, S: Storage<SignalData<T>>> PartialEq for Signal<T, S> {
  209. fn eq(&self, other: &Self) -> bool {
  210. self.inner == other.inner
  211. }
  212. }
  213. /// Allow calling a signal with signal() syntax
  214. ///
  215. /// Currently only limited to copy types, though could probably specialize for string/arc/rc
  216. impl<T: Clone, S: Storage<SignalData<T>> + 'static> Deref for Signal<T, S> {
  217. type Target = dyn Fn() -> T;
  218. fn deref(&self) -> &Self::Target {
  219. Readable::deref_impl(self)
  220. }
  221. }
  222. #[cfg(feature = "serde")]
  223. impl<T: serde::Serialize + 'static, Store: Storage<SignalData<T>>> serde::Serialize
  224. for Signal<T, Store>
  225. {
  226. fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
  227. self.read().serialize(serializer)
  228. }
  229. }
  230. #[cfg(feature = "serde")]
  231. impl<'de, T: serde::Deserialize<'de> + 'static, Store: Storage<SignalData<T>>>
  232. serde::Deserialize<'de> for Signal<T, Store>
  233. {
  234. fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
  235. Ok(Self::new_maybe_sync(T::deserialize(deserializer)?))
  236. }
  237. }
  238. /// A mutable reference to a signal's value.
  239. ///
  240. /// T is the current type of the write
  241. /// S is the storage type of the signal
  242. pub struct Write<T: ?Sized + 'static, S: AnyStorage = UnsyncStorage> {
  243. write: S::Mut<T>,
  244. drop_signal: Box<dyn Any>,
  245. }
  246. impl<T: ?Sized + 'static, S: AnyStorage> Write<T, S> {
  247. /// Map the mutable reference to the signal's value to a new type.
  248. pub fn map<O: ?Sized>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<O, S> {
  249. let Self {
  250. write, drop_signal, ..
  251. } = myself;
  252. Write {
  253. write: S::map_mut(write, f),
  254. drop_signal,
  255. }
  256. }
  257. /// Try to map the mutable reference to the signal's value to a new type
  258. pub fn filter_map<O: ?Sized>(
  259. myself: Self,
  260. f: impl FnOnce(&mut T) -> Option<&mut O>,
  261. ) -> Option<Write<O, S>> {
  262. let Self {
  263. write, drop_signal, ..
  264. } = myself;
  265. let write = S::try_map_mut(write, f);
  266. write.map(|write| Write { write, drop_signal })
  267. }
  268. }
  269. impl<T: ?Sized + 'static, S: AnyStorage> Deref for Write<T, S> {
  270. type Target = T;
  271. fn deref(&self) -> &Self::Target {
  272. &self.write
  273. }
  274. }
  275. impl<T: ?Sized, S: AnyStorage> DerefMut for Write<T, S> {
  276. fn deref_mut(&mut self) -> &mut Self::Target {
  277. &mut self.write
  278. }
  279. }
  280. struct SignalSubscriberDrop<T: 'static, S: Storage<SignalData<T>>> {
  281. signal: Signal<T, S>,
  282. #[cfg(debug_assertions)]
  283. origin: &'static std::panic::Location<'static>,
  284. }
  285. impl<T: 'static, S: Storage<SignalData<T>>> Drop for SignalSubscriberDrop<T, S> {
  286. fn drop(&mut self) {
  287. #[cfg(debug_assertions)]
  288. tracing::trace!(
  289. "Write on signal at {:?} finished, updating subscribers",
  290. self.origin
  291. );
  292. self.signal.update_subscribers();
  293. }
  294. }