signal.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. use std::{
  2. cell::RefCell,
  3. ops::{Deref, DerefMut},
  4. panic::Location,
  5. rc::Rc,
  6. sync::Arc,
  7. };
  8. use dioxus_core::{
  9. prelude::{current_scope_id, has_context, provide_context, schedule_update_any},
  10. ScopeId, ScopeState,
  11. };
  12. use generational_box::{GenerationalRef, GenerationalRefMut};
  13. use crate::{CopyValue, Effect};
  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. /// fn App(cx: Scope) -> Element {
  21. /// let mut count = use_signal(cx, || 0);
  22. ///
  23. /// // 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.
  24. /// // The app component will never be rerendered in this example.
  25. /// render! { Child { state: count } }
  26. /// }
  27. ///
  28. /// #[inline_props]
  29. /// fn Child(cx: Scope, state: Signal<u32>) -> Element {
  30. /// let state = *state;
  31. ///
  32. /// use_future!(cx, |()| async move {
  33. /// // Because the signal is a Copy type, we can use it in an async block without cloning it.
  34. /// *state.write() += 1;
  35. /// });
  36. ///
  37. /// render! {
  38. /// button {
  39. /// onclick: move |_| *state.write() += 1,
  40. /// "{state}"
  41. /// }
  42. /// }
  43. /// }
  44. /// ```
  45. #[track_caller]
  46. pub fn use_signal<T: 'static>(cx: &ScopeState, f: impl FnOnce() -> T) -> Signal<T> {
  47. #[cfg(debug_assertions)]
  48. let caller = Location::caller();
  49. *cx.use_hook(|| {
  50. Signal::new_with_caller(
  51. f(),
  52. #[cfg(debug_assertions)]
  53. caller,
  54. )
  55. })
  56. }
  57. #[derive(Clone)]
  58. struct Unsubscriber {
  59. scope: ScopeId,
  60. subscribers: UnsubscriberArray,
  61. }
  62. type UnsubscriberArray = Rc<RefCell<Vec<Rc<RefCell<Vec<ScopeId>>>>>>;
  63. impl Drop for Unsubscriber {
  64. fn drop(&mut self) {
  65. for subscribers in self.subscribers.borrow().iter() {
  66. subscribers.borrow_mut().retain(|s| *s != self.scope);
  67. }
  68. }
  69. }
  70. fn current_unsubscriber() -> Unsubscriber {
  71. match has_context() {
  72. Some(rt) => rt,
  73. None => {
  74. let owner = Unsubscriber {
  75. scope: current_scope_id().expect("in a virtual dom"),
  76. subscribers: Default::default(),
  77. };
  78. provide_context(owner).expect("in a virtual dom")
  79. }
  80. }
  81. }
  82. pub(crate) struct SignalData<T> {
  83. pub(crate) subscribers: Rc<RefCell<Vec<ScopeId>>>,
  84. pub(crate) effect_subscribers: Rc<RefCell<Vec<Effect>>>,
  85. pub(crate) update_any: Arc<dyn Fn(ScopeId)>,
  86. pub(crate) value: T,
  87. }
  88. /// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
  89. ///
  90. /// ```rust
  91. /// use dioxus::prelude::*;
  92. /// use dioxus_signals::*;
  93. ///
  94. /// fn App(cx: Scope) -> Element {
  95. /// let mut count = use_signal(cx, || 0);
  96. ///
  97. /// // 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.
  98. /// // The app component will never be rerendered in this example.
  99. /// render! { Child { state: count } }
  100. /// }
  101. ///
  102. /// #[inline_props]
  103. /// fn Child(cx: Scope, state: Signal<u32>) -> Element {
  104. /// let state = *state;
  105. ///
  106. /// use_future!(cx, |()| async move {
  107. /// // Because the signal is a Copy type, we can use it in an async block without cloning it.
  108. /// *state.write() += 1;
  109. /// });
  110. ///
  111. /// render! {
  112. /// button {
  113. /// onclick: move |_| *state.write() += 1,
  114. /// "{state}"
  115. /// }
  116. /// }
  117. /// }
  118. /// ```
  119. pub struct Signal<T: 'static> {
  120. pub(crate) inner: CopyValue<SignalData<T>>,
  121. }
  122. #[cfg(feature = "serde")]
  123. impl<T: serde::Serialize + 'static> serde::Serialize for Signal<T> {
  124. fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
  125. self.read().serialize(serializer)
  126. }
  127. }
  128. #[cfg(feature = "serde")]
  129. impl<'de, T: serde::Deserialize<'de> + 'static> serde::Deserialize<'de> for Signal<T> {
  130. fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
  131. Ok(Self::new(T::deserialize(deserializer)?))
  132. }
  133. }
  134. impl<T: 'static> Signal<T> {
  135. /// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
  136. #[track_caller]
  137. pub fn new(value: T) -> Self {
  138. Self {
  139. inner: CopyValue::new(SignalData {
  140. subscribers: Default::default(),
  141. effect_subscribers: Default::default(),
  142. update_any: schedule_update_any().expect("in a virtual dom"),
  143. value,
  144. }),
  145. }
  146. }
  147. /// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
  148. fn new_with_caller(
  149. value: T,
  150. #[cfg(debug_assertions)] caller: &'static Location<'static>,
  151. ) -> Self {
  152. Self {
  153. inner: CopyValue::new_with_caller(
  154. SignalData {
  155. subscribers: Default::default(),
  156. effect_subscribers: Default::default(),
  157. update_any: schedule_update_any().expect("in a virtual dom"),
  158. value,
  159. },
  160. #[cfg(debug_assertions)]
  161. caller,
  162. ),
  163. }
  164. }
  165. /// Get the scope the signal was created in.
  166. pub fn origin_scope(&self) -> ScopeId {
  167. self.inner.origin_scope()
  168. }
  169. /// Get the current value of the signal. This will subscribe the current scope to the signal.
  170. /// If the signal has been dropped, this will panic.
  171. #[track_caller]
  172. pub fn read(&self) -> GenerationalRef<T> {
  173. let inner = self.inner.read();
  174. if let Some(effect) = Effect::current() {
  175. let mut effect_subscribers = inner.effect_subscribers.borrow_mut();
  176. if !effect_subscribers.contains(&effect) {
  177. effect_subscribers.push(effect);
  178. }
  179. } else if let Some(current_scope_id) = current_scope_id() {
  180. // only subscribe if the vdom is rendering
  181. if dioxus_core::vdom_is_rendering() {
  182. log::trace!(
  183. "{:?} subscribed to {:?}",
  184. self.inner.value,
  185. current_scope_id
  186. );
  187. let mut subscribers = inner.subscribers.borrow_mut();
  188. if !subscribers.contains(&current_scope_id) {
  189. subscribers.push(current_scope_id);
  190. drop(subscribers);
  191. let unsubscriber = current_unsubscriber();
  192. inner.subscribers.borrow_mut().push(unsubscriber.scope);
  193. }
  194. }
  195. }
  196. GenerationalRef::map(inner, |v| &v.value)
  197. }
  198. /// Get a mutable reference to the signal's value.
  199. /// If the signal has been dropped, this will panic.
  200. #[track_caller]
  201. pub fn write(&self) -> Write<'_, T> {
  202. let inner = self.inner.write();
  203. let borrow = GenerationalRefMut::map(inner, |v| &mut v.value);
  204. Write {
  205. write: borrow,
  206. signal: SignalSubscriberDrop { signal: *self },
  207. }
  208. }
  209. fn update_subscribers(&self) {
  210. {
  211. let inner = self.inner.read();
  212. for &scope_id in &*inner.subscribers.borrow() {
  213. log::trace!(
  214. "Write on {:?} triggered update on {:?}",
  215. self.inner.value,
  216. scope_id
  217. );
  218. (inner.update_any)(scope_id);
  219. }
  220. }
  221. let subscribers = {
  222. let self_read = self.inner.read();
  223. let mut effects = self_read.effect_subscribers.borrow_mut();
  224. std::mem::take(&mut *effects)
  225. };
  226. for effect in subscribers {
  227. log::trace!(
  228. "Write on {:?} triggered effect {:?}",
  229. self.inner.value,
  230. effect
  231. );
  232. effect.try_run();
  233. }
  234. }
  235. /// Set the value of the signal. This will trigger an update on all subscribers.
  236. #[track_caller]
  237. pub fn set(&self, value: T) {
  238. *self.write() = value;
  239. }
  240. /// Run a closure with a reference to the signal's value.
  241. /// If the signal has been dropped, this will panic.
  242. #[track_caller]
  243. pub fn with<O>(&self, f: impl FnOnce(&T) -> O) -> O {
  244. let write = self.read();
  245. f(&*write)
  246. }
  247. /// Run a closure with a mutable reference to the signal's value.
  248. /// If the signal has been dropped, this will panic.
  249. #[track_caller]
  250. pub fn with_mut<O>(&self, f: impl FnOnce(&mut T) -> O) -> O {
  251. let mut write = self.write();
  252. f(&mut *write)
  253. }
  254. }
  255. impl<T: Clone + 'static> Signal<T> {
  256. /// Get the current value of the signal. This will subscribe the current scope to the signal.
  257. /// If the signal has been dropped, this will panic.
  258. #[track_caller]
  259. pub fn value(&self) -> T {
  260. self.read().clone()
  261. }
  262. }
  263. impl<T: 'static> PartialEq for Signal<T> {
  264. fn eq(&self, other: &Self) -> bool {
  265. self.inner == other.inner
  266. }
  267. }
  268. struct SignalSubscriberDrop<T: 'static> {
  269. signal: Signal<T>,
  270. }
  271. impl<T: 'static> Drop for SignalSubscriberDrop<T> {
  272. fn drop(&mut self) {
  273. self.signal.update_subscribers();
  274. }
  275. }
  276. /// A mutable reference to a signal's value.
  277. pub struct Write<'a, T: 'static, I: 'static = T> {
  278. write: GenerationalRefMut<'a, T>,
  279. signal: SignalSubscriberDrop<I>,
  280. }
  281. impl<'a, T: 'static, I: 'static> Write<'a, T, I> {
  282. /// Map the mutable reference to the signal's value to a new type.
  283. pub fn map<O>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<'a, O, I> {
  284. let Self { write, signal } = myself;
  285. Write {
  286. write: GenerationalRefMut::map(write, f),
  287. signal,
  288. }
  289. }
  290. /// Try to map the mutable reference to the signal's value to a new type
  291. pub fn filter_map<O>(
  292. myself: Self,
  293. f: impl FnOnce(&mut T) -> Option<&mut O>,
  294. ) -> Option<Write<'a, O, I>> {
  295. let Self { write, signal } = myself;
  296. let write = GenerationalRefMut::filter_map(write, f);
  297. write.map(|write| Write { write, signal })
  298. }
  299. }
  300. impl<'a, T: 'static> Deref for Write<'a, T> {
  301. type Target = T;
  302. fn deref(&self) -> &Self::Target {
  303. &self.write
  304. }
  305. }
  306. impl<T> DerefMut for Write<'_, T> {
  307. fn deref_mut(&mut self) -> &mut Self::Target {
  308. &mut self.write
  309. }
  310. }
  311. /// A signal that can only be read from.
  312. pub struct ReadOnlySignal<T: 'static> {
  313. inner: Signal<T>,
  314. }
  315. impl<T: 'static> ReadOnlySignal<T> {
  316. /// Create a new read-only signal.
  317. pub fn new(signal: Signal<T>) -> Self {
  318. Self { inner: signal }
  319. }
  320. /// Get the scope that the signal was created in.
  321. pub fn origin_scope(&self) -> ScopeId {
  322. self.inner.origin_scope()
  323. }
  324. /// Get the current value of the signal. This will subscribe the current scope to the signal.
  325. #[track_caller]
  326. pub fn read(&self) -> GenerationalRef<T> {
  327. self.inner.read()
  328. }
  329. /// Run a closure with a reference to the signal's value.
  330. #[track_caller]
  331. pub fn with<O>(&self, f: impl FnOnce(&T) -> O) -> O {
  332. self.inner.with(f)
  333. }
  334. }
  335. impl<T: Clone + 'static> ReadOnlySignal<T> {
  336. /// Get the current value of the signal. This will subscribe the current scope to the signal.
  337. pub fn value(&self) -> T {
  338. self.read().clone()
  339. }
  340. }
  341. impl<T: 'static> PartialEq for ReadOnlySignal<T> {
  342. fn eq(&self, other: &Self) -> bool {
  343. self.inner == other.inner
  344. }
  345. }