signal.rs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. use crate::{
  2. get_effect_ref, read::Readable, write::Writable, CopyValue, Effect, EffectInner,
  3. EffectStackRef, GlobalMemo, GlobalSignal, MappedSignal, ReadOnlySignal, EFFECT_STACK,
  4. };
  5. use dioxus_core::{
  6. prelude::{
  7. current_scope_id, has_context, provide_context, schedule_update_any, IntoAttributeValue,
  8. },
  9. ScopeId,
  10. };
  11. use generational_box::{AnyStorage, GenerationalBoxId, Storage, SyncStorage, UnsyncStorage};
  12. use parking_lot::RwLock;
  13. use std::{
  14. any::Any,
  15. cell::RefCell,
  16. ops::{Deref, DerefMut},
  17. rc::Rc,
  18. sync::Arc,
  19. };
  20. /// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
  21. ///
  22. /// ```rust
  23. /// use dioxus::prelude::*;
  24. /// use dioxus_signals::*;
  25. ///
  26. /// #[component]
  27. /// fn App() -> Element {
  28. /// let mut count = use_signal(|| 0);
  29. ///
  30. /// // 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.
  31. /// // The app component will never be rerendered in this example.
  32. /// rsx! { Child { state: count } }
  33. /// }
  34. ///
  35. /// #[component]
  36. /// fn Child(state: Signal<u32>) -> Element {
  37. /// let state = *state;
  38. ///
  39. /// use_future( |()| async move {
  40. /// // Because the signal is a Copy type, we can use it in an async block without cloning it.
  41. /// *state.write() += 1;
  42. /// });
  43. ///
  44. /// rsx! {
  45. /// button {
  46. /// onclick: move |_| *state.write() += 1,
  47. /// "{state}"
  48. /// }
  49. /// }
  50. /// }
  51. /// ```
  52. pub struct Signal<T: 'static, S: Storage<SignalData<T>> = UnsyncStorage> {
  53. pub(crate) inner: CopyValue<SignalData<T>, S>,
  54. }
  55. /// A signal that can safely shared between threads.
  56. pub type SyncSignal<T> = Signal<T, SyncStorage>;
  57. /// The data stored for tracking in a signal.
  58. pub struct SignalData<T> {
  59. pub(crate) subscribers: Arc<RwLock<SignalSubscribers>>,
  60. pub(crate) update_any: Arc<dyn Fn(ScopeId) + Sync + Send>,
  61. pub(crate) effect_ref: EffectStackRef,
  62. pub(crate) value: T,
  63. }
  64. #[derive(Default)]
  65. pub(crate) struct SignalSubscribers {
  66. pub(crate) subscribers: Vec<ScopeId>,
  67. pub(crate) effect_subscribers: Vec<GenerationalBoxId>,
  68. }
  69. impl<T: 'static> Signal<T> {
  70. /// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
  71. #[track_caller]
  72. pub fn new(value: T) -> Self {
  73. Self::new_maybe_sync(value)
  74. }
  75. /// 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.
  76. #[track_caller]
  77. pub fn new_in_scope(value: T, owner: ScopeId) -> Self {
  78. Self::new_maybe_sync_in_scope(value, owner)
  79. }
  80. /// Creates a new global Signal that can be used in a global static.
  81. #[track_caller]
  82. pub const fn global(constructor: fn() -> T) -> GlobalSignal<T> {
  83. GlobalSignal::new(constructor)
  84. }
  85. }
  86. impl<T: PartialEq + 'static> Signal<T> {
  87. /// Creates a new global Signal that can be used in a global static.
  88. #[track_caller]
  89. pub const fn global_memo(constructor: fn() -> T) -> GlobalMemo<T>
  90. where
  91. T: PartialEq,
  92. {
  93. GlobalMemo::new(constructor)
  94. }
  95. /// Creates a new unsync Selector. The selector will be run immediately and whenever any signal it reads changes.
  96. ///
  97. /// Selectors can be used to efficiently compute derived data from signals.
  98. #[track_caller]
  99. pub fn selector(f: impl FnMut() -> T + 'static) -> ReadOnlySignal<T> {
  100. Self::maybe_sync_memo(f)
  101. }
  102. /// Creates a new Selector that may be Sync + Send. The selector will be run immediately and whenever any signal it reads changes.
  103. ///
  104. /// Selectors can be used to efficiently compute derived data from signals.
  105. #[track_caller]
  106. pub fn maybe_sync_memo<S: Storage<SignalData<T>>>(
  107. mut f: impl FnMut() -> T + 'static,
  108. ) -> ReadOnlySignal<T, S> {
  109. let effect = Effect {
  110. source: current_scope_id().expect("in a virtual dom"),
  111. inner: CopyValue::invalid(),
  112. };
  113. {
  114. EFFECT_STACK.with(|stack| stack.effects.write().push(effect));
  115. }
  116. let mut state: Signal<T, S> = Signal::new_maybe_sync(f());
  117. {
  118. EFFECT_STACK.with(|stack| stack.effects.write().pop());
  119. }
  120. let invalid_id = effect.id();
  121. tracing::trace!("Creating effect: {:?}", invalid_id);
  122. effect.inner.value.set(EffectInner {
  123. callback: Box::new(move || {
  124. let value = f();
  125. let changed = {
  126. let old = state.inner.read();
  127. value != old.value
  128. };
  129. if changed {
  130. state.set(value)
  131. }
  132. }),
  133. id: invalid_id,
  134. });
  135. {
  136. EFFECT_STACK.with(|stack| stack.effect_mapping.write().insert(invalid_id, effect));
  137. }
  138. ReadOnlySignal::new_maybe_sync(state)
  139. }
  140. }
  141. impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
  142. /// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
  143. #[track_caller]
  144. #[tracing::instrument(skip(value))]
  145. pub fn new_maybe_sync(value: T) -> Self {
  146. Self {
  147. inner: CopyValue::<SignalData<T>, S>::new_maybe_sync(SignalData {
  148. subscribers: Default::default(),
  149. update_any: schedule_update_any(),
  150. value,
  151. effect_ref: get_effect_ref(),
  152. }),
  153. }
  154. }
  155. /// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
  156. pub fn new_with_caller(
  157. value: T,
  158. #[cfg(debug_assertions)] caller: &'static std::panic::Location<'static>,
  159. ) -> Self {
  160. Self {
  161. inner: CopyValue::new_with_caller(
  162. SignalData {
  163. subscribers: Default::default(),
  164. update_any: schedule_update_any(),
  165. value,
  166. effect_ref: get_effect_ref(),
  167. },
  168. #[cfg(debug_assertions)]
  169. caller,
  170. ),
  171. }
  172. }
  173. /// 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.
  174. #[track_caller]
  175. #[tracing::instrument(skip(value))]
  176. pub fn new_maybe_sync_in_scope(value: T, owner: ScopeId) -> Self {
  177. Self {
  178. inner: CopyValue::<SignalData<T>, S>::new_maybe_sync_in_scope(
  179. SignalData {
  180. subscribers: Default::default(),
  181. update_any: schedule_update_any(),
  182. value,
  183. effect_ref: get_effect_ref(),
  184. },
  185. owner,
  186. ),
  187. }
  188. }
  189. /// Take the value out of the signal, invalidating the signal in the process.
  190. pub fn take(&self) -> T {
  191. self.inner.take().value
  192. }
  193. /// Get the scope the signal was created in.
  194. pub fn origin_scope(&self) -> ScopeId {
  195. self.inner.origin_scope()
  196. }
  197. fn update_subscribers(&self) {
  198. {
  199. let inner = self.inner.read();
  200. for &scope_id in &*inner.subscribers.read().subscribers {
  201. tracing::trace!(
  202. "Write on {:?} triggered update on {:?}",
  203. self.inner.value,
  204. scope_id
  205. );
  206. (inner.update_any)(scope_id);
  207. }
  208. }
  209. let self_read = &self.inner.read();
  210. let subscribers = {
  211. let effects = &mut self_read.subscribers.write().effect_subscribers;
  212. std::mem::take(&mut *effects)
  213. };
  214. let effect_ref = &self_read.effect_ref;
  215. for effect in subscribers {
  216. tracing::trace!(
  217. "Write on {:?} triggered effect {:?}",
  218. self.inner.value,
  219. effect
  220. );
  221. effect_ref.rerun_effect(effect);
  222. }
  223. }
  224. /// Unsubscribe this scope from the signal's effect list
  225. pub fn unsubscribe(&self, scope: ScopeId) {
  226. self.inner
  227. .read()
  228. .subscribers
  229. .write()
  230. .subscribers
  231. .retain(|s| *s != scope);
  232. }
  233. /// Map the signal to a new type.
  234. pub fn map<O>(self, f: impl Fn(&T) -> &O + 'static) -> MappedSignal<S::Ref<O>> {
  235. MappedSignal::new(self, f)
  236. }
  237. /// Get the generational id of the signal.
  238. pub fn id(&self) -> generational_box::GenerationalBoxId {
  239. self.inner.id()
  240. }
  241. }
  242. impl<T, S: Storage<SignalData<T>>> Readable<T> for Signal<T, S> {
  243. type Ref<R: ?Sized + 'static> = S::Ref<R>;
  244. fn map_ref<I, U: ?Sized, F: FnOnce(&I) -> &U>(ref_: Self::Ref<I>, f: F) -> Self::Ref<U> {
  245. S::map(ref_, f)
  246. }
  247. fn try_map_ref<I, U: ?Sized, F: FnOnce(&I) -> Option<&U>>(
  248. ref_: Self::Ref<I>,
  249. f: F,
  250. ) -> Option<Self::Ref<U>> {
  251. S::try_map(ref_, f)
  252. }
  253. /// Get the current value of the signal. This will subscribe the current scope to the signal.
  254. /// If you would like to read the signal without subscribing to it, you can use [`Self::peek`] instead.
  255. ///
  256. /// If the signal has been dropped, this will panic.
  257. #[track_caller]
  258. fn read(&self) -> S::Ref<T> {
  259. let inner = self.inner.read();
  260. if let Some(effect) = EFFECT_STACK.with(|stack| stack.current()) {
  261. let subscribers = inner.subscribers.read();
  262. if !subscribers.effect_subscribers.contains(&effect.inner.id()) {
  263. drop(subscribers);
  264. let mut subscribers = inner.subscribers.write();
  265. subscribers.effect_subscribers.push(effect.inner.id());
  266. }
  267. } else if let Some(current_scope_id) = current_scope_id() {
  268. // only subscribe if the vdom is rendering
  269. if dioxus_core::vdom_is_rendering() {
  270. tracing::trace!(
  271. "{:?} subscribed to {:?}",
  272. self.inner.value,
  273. current_scope_id
  274. );
  275. let subscribers = inner.subscribers.read();
  276. if !subscribers.subscribers.contains(&current_scope_id) {
  277. drop(subscribers);
  278. let mut subscribers = inner.subscribers.write();
  279. subscribers.subscribers.push(current_scope_id);
  280. let unsubscriber = current_unsubscriber();
  281. subscribers.subscribers.push(unsubscriber.borrow().scope);
  282. }
  283. }
  284. }
  285. S::map(inner, |v| &v.value)
  286. }
  287. /// 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.**
  288. ///
  289. /// If the signal has been dropped, this will panic.
  290. fn peek(&self) -> S::Ref<T> {
  291. let inner = self.inner.read();
  292. S::map(inner, |v| &v.value)
  293. }
  294. }
  295. impl<T: 'static, S: Storage<SignalData<T>>> Writable<T> for Signal<T, S> {
  296. type Mut<R: ?Sized + 'static> = Write<R, S>;
  297. fn map_mut<I, U: ?Sized + 'static, F: FnOnce(&mut I) -> &mut U>(
  298. ref_: Self::Mut<I>,
  299. f: F,
  300. ) -> Self::Mut<U> {
  301. Write::map(ref_, f)
  302. }
  303. fn try_map_mut<I: 'static, U: ?Sized + 'static, F: FnOnce(&mut I) -> Option<&mut U>>(
  304. ref_: Self::Mut<I>,
  305. f: F,
  306. ) -> Option<Self::Mut<U>> {
  307. Write::filter_map(ref_, f)
  308. }
  309. #[track_caller]
  310. fn try_write(&self) -> Result<Self::Mut<T>, generational_box::BorrowMutError> {
  311. self.inner.try_write().map(|inner| {
  312. let borrow = S::map_mut(inner, |v| &mut v.value);
  313. Write {
  314. write: borrow,
  315. drop_signal: Box::new(SignalSubscriberDrop { signal: *self }),
  316. }
  317. })
  318. }
  319. }
  320. impl<T> IntoAttributeValue for Signal<T>
  321. where
  322. T: Clone + IntoAttributeValue,
  323. {
  324. fn into_value(self) -> dioxus_core::AttributeValue {
  325. self.with(|f| f.clone().into_value())
  326. }
  327. }
  328. impl<T: 'static, S: Storage<SignalData<T>>> PartialEq for Signal<T, S> {
  329. fn eq(&self, other: &Self) -> bool {
  330. self.inner == other.inner
  331. }
  332. }
  333. /// Allow calling a signal with signal() syntax
  334. ///
  335. /// Currently only limited to copy types, though could probably specialize for string/arc/rc
  336. impl<T: Clone, S: Storage<SignalData<T>> + 'static> Deref for Signal<T, S> {
  337. type Target = dyn Fn() -> T;
  338. fn deref(&self) -> &Self::Target {
  339. Readable::deref_impl(self)
  340. }
  341. }
  342. #[cfg(feature = "serde")]
  343. impl<T: serde::Serialize + 'static, Store: Storage<SignalData<T>>> serde::Serialize
  344. for Signal<T, Store>
  345. {
  346. fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
  347. self.read().serialize(serializer)
  348. }
  349. }
  350. #[cfg(feature = "serde")]
  351. impl<'de, T: serde::Deserialize<'de> + 'static, Store: Storage<SignalData<T>>>
  352. serde::Deserialize<'de> for Signal<T, Store>
  353. {
  354. fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
  355. Ok(Self::new_maybe_sync(T::deserialize(deserializer)?))
  356. }
  357. }
  358. struct Unsubscriber {
  359. scope: ScopeId,
  360. subscribers: UnsubscriberArray,
  361. }
  362. type UnsubscriberArray = Vec<Rc<RefCell<Vec<ScopeId>>>>;
  363. impl Drop for Unsubscriber {
  364. fn drop(&mut self) {
  365. for subscribers in &self.subscribers {
  366. subscribers.borrow_mut().retain(|s| *s != self.scope);
  367. }
  368. }
  369. }
  370. fn current_unsubscriber() -> Rc<RefCell<Unsubscriber>> {
  371. match has_context() {
  372. Some(rt) => rt,
  373. None => {
  374. let owner = Unsubscriber {
  375. scope: current_scope_id().expect("in a virtual dom"),
  376. subscribers: Default::default(),
  377. };
  378. provide_context(Rc::new(RefCell::new(owner)))
  379. }
  380. }
  381. }
  382. struct SignalSubscriberDrop<T: 'static, S: Storage<SignalData<T>>> {
  383. signal: Signal<T, S>,
  384. }
  385. impl<T: 'static, S: Storage<SignalData<T>>> Drop for SignalSubscriberDrop<T, S> {
  386. fn drop(&mut self) {
  387. self.signal.update_subscribers();
  388. }
  389. }
  390. /// A mutable reference to a signal's value.
  391. ///
  392. /// T is the current type of the write
  393. /// S is the storage type of the signal
  394. pub struct Write<T: ?Sized + 'static, S: AnyStorage = UnsyncStorage> {
  395. write: S::Mut<T>,
  396. drop_signal: Box<dyn Any>,
  397. }
  398. impl<T: ?Sized + 'static, S: AnyStorage> Write<T, S> {
  399. /// Map the mutable reference to the signal's value to a new type.
  400. pub fn map<O: ?Sized>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<O, S> {
  401. let Self {
  402. write, drop_signal, ..
  403. } = myself;
  404. Write {
  405. write: S::map_mut(write, f),
  406. drop_signal,
  407. }
  408. }
  409. /// Try to map the mutable reference to the signal's value to a new type
  410. pub fn filter_map<O: ?Sized>(
  411. myself: Self,
  412. f: impl FnOnce(&mut T) -> Option<&mut O>,
  413. ) -> Option<Write<O, S>> {
  414. let Self {
  415. write, drop_signal, ..
  416. } = myself;
  417. let write = S::try_map_mut(write, f);
  418. write.map(|write| Write { write, drop_signal })
  419. }
  420. }
  421. impl<T: ?Sized + 'static, S: AnyStorage> Deref for Write<T, S> {
  422. type Target = T;
  423. fn deref(&self) -> &Self::Target {
  424. &self.write
  425. }
  426. }
  427. impl<T: ?Sized, S: AnyStorage> DerefMut for Write<T, S> {
  428. fn deref_mut(&mut self) -> &mut Self::Target {
  429. &mut self.write
  430. }
  431. }