dioxus_signals/signal.rs
1use crate::{default_impl, fmt_impls, write_impls, Global};
2use crate::{read::*, write::*, CopyValue, GlobalMemo, GlobalSignal, ReadableRef};
3use crate::{Memo, WritableRef};
4use dioxus_core::prelude::*;
5use generational_box::{AnyStorage, BorrowResult, Storage, SyncStorage, UnsyncStorage};
6use std::sync::Arc;
7use std::{
8 any::Any,
9 collections::HashSet,
10 ops::{Deref, DerefMut},
11 sync::Mutex,
12};
13
14#[doc = include_str!("../docs/signals.md")]
15#[doc(alias = "State")]
16#[doc(alias = "UseState")]
17#[doc(alias = "UseRef")]
18pub struct Signal<T: 'static, S: Storage<SignalData<T>> = UnsyncStorage> {
19 pub(crate) inner: CopyValue<SignalData<T>, S>,
20}
21
22/// A signal that can safely shared between threads.
23#[doc(alias = "SendSignal")]
24#[doc(alias = "UseRwLock")]
25#[doc(alias = "UseRw")]
26#[doc(alias = "UseMutex")]
27pub type SyncSignal<T> = Signal<T, SyncStorage>;
28
29/// The data stored for tracking in a signal.
30pub struct SignalData<T> {
31 pub(crate) subscribers: Arc<Mutex<HashSet<ReactiveContext>>>,
32 pub(crate) value: T,
33}
34
35impl<T: 'static> Signal<T> {
36 /// Creates a new [`Signal`]. Signals are a Copy state management solution with automatic dependency tracking.
37 ///
38 /// <div class="warning">
39 ///
40 /// This function should generally only be called inside hooks. The signal that this function creates is owned by the current component and will only be dropped when the component is dropped. If you call this function outside of a hook many times, you will leak memory until the component is dropped.
41 ///
42 /// ```rust
43 /// # use dioxus::prelude::*;
44 /// fn MyComponent() {
45 /// // ❌ Every time MyComponent runs, it will create a new signal that is only dropped when MyComponent is dropped
46 /// let signal = Signal::new(0);
47 /// use_context_provider(|| signal);
48 /// // ✅ Since the use_context_provider hook only runs when the component is created, the signal will only be created once and it will be dropped when MyComponent is dropped
49 /// let signal = use_context_provider(|| Signal::new(0));
50 /// }
51 /// ```
52 ///
53 /// </div>
54 #[track_caller]
55 pub fn new(value: T) -> Self {
56 Self::new_maybe_sync(value)
57 }
58
59 /// 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.
60 #[track_caller]
61 pub fn new_in_scope(value: T, owner: ScopeId) -> Self {
62 Self::new_maybe_sync_in_scope(value, owner)
63 }
64
65 /// Creates a new [`GlobalSignal`] that can be used anywhere inside your dioxus app. This signal will automatically be created once per app the first time you use it.
66 ///
67 /// # Example
68 /// ```rust, no_run
69 /// # use dioxus::prelude::*;
70 /// // Create a new global signal that can be used anywhere in your app
71 /// static SIGNAL: GlobalSignal<i32> = Signal::global(|| 0);
72 ///
73 /// fn App() -> Element {
74 /// rsx! {
75 /// button {
76 /// onclick: move |_| *SIGNAL.write() += 1,
77 /// "{SIGNAL}"
78 /// }
79 /// }
80 /// }
81 /// ```
82 ///
83 /// <div class="warning">
84 ///
85 /// Global signals are generally not recommended for use in libraries because it makes it more difficult to allow multiple instances of components you define in your library.
86 ///
87 /// </div>
88 #[track_caller]
89 pub const fn global(constructor: fn() -> T) -> GlobalSignal<T> {
90 Global::new(constructor)
91 }
92}
93
94impl<T: PartialEq + 'static> Signal<T> {
95 /// Creates a new [`GlobalMemo`] that can be used anywhere inside your dioxus app. This memo will automatically be created once per app the first time you use it.
96 ///
97 /// # Example
98 /// ```rust, no_run
99 /// # use dioxus::prelude::*;
100 /// static SIGNAL: GlobalSignal<i32> = Signal::global(|| 0);
101 /// // Create a new global memo that can be used anywhere in your app
102 /// static DOUBLED: GlobalMemo<i32> = Signal::global_memo(|| SIGNAL() * 2);
103 ///
104 /// fn App() -> Element {
105 /// rsx! {
106 /// button {
107 /// // When SIGNAL changes, the memo will update because the SIGNAL is read inside DOUBLED
108 /// onclick: move |_| *SIGNAL.write() += 1,
109 /// "{DOUBLED}"
110 /// }
111 /// }
112 /// }
113 /// ```
114 ///
115 /// <div class="warning">
116 ///
117 /// Global memos are generally not recommended for use in libraries because it makes it more difficult to allow multiple instances of components you define in your library.
118 ///
119 /// </div>
120 #[track_caller]
121 pub const fn global_memo(constructor: fn() -> T) -> GlobalMemo<T>
122 where
123 T: PartialEq,
124 {
125 GlobalMemo::new(constructor)
126 }
127
128 /// Creates a new unsync Selector. 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 memo(f: impl FnMut() -> T + 'static) -> Memo<T> {
133 Memo::new(f)
134 }
135
136 /// Creates a new unsync Selector with an explicit location. The selector will be run immediately and whenever any signal it reads changes.
137 ///
138 /// Selectors can be used to efficiently compute derived data from signals.
139 pub fn memo_with_location(
140 f: impl FnMut() -> T + 'static,
141 location: &'static std::panic::Location<'static>,
142 ) -> Memo<T> {
143 Memo::new_with_location(f, location)
144 }
145}
146
147impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
148 /// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
149 #[track_caller]
150 #[tracing::instrument(skip(value))]
151 pub fn new_maybe_sync(value: T) -> Self {
152 Self {
153 inner: CopyValue::<SignalData<T>, S>::new_maybe_sync(SignalData {
154 subscribers: Default::default(),
155 value,
156 }),
157 }
158 }
159
160 /// Creates a new Signal with an explicit caller. Signals are a Copy state management solution with automatic dependency tracking.
161 ///
162 /// This method can be used to provide the correct caller information for signals that are created in closures:
163 ///
164 /// ```rust
165 /// # use dioxus::prelude::*;
166 /// #[track_caller]
167 /// fn use_my_signal(function: impl FnOnce() -> i32) -> Signal<i32> {
168 /// // We capture the caller information outside of the closure so that it points to the caller of use_my_custom_hook instead of the closure
169 /// let caller = std::panic::Location::caller();
170 /// use_hook(move || Signal::new_with_caller(function(), caller))
171 /// }
172 /// ```
173 pub fn new_with_caller(value: T, caller: &'static std::panic::Location<'static>) -> Self {
174 Self {
175 inner: CopyValue::new_with_caller(
176 SignalData {
177 subscribers: Default::default(),
178 value,
179 },
180 caller,
181 ),
182 }
183 }
184
185 /// Create a new Signal without an owner. This will leak memory if you don't manually drop it.
186 pub fn leak_with_caller(value: T, caller: &'static std::panic::Location<'static>) -> Self {
187 Self {
188 inner: CopyValue::leak_with_caller(
189 SignalData {
190 subscribers: Default::default(),
191 value,
192 },
193 caller,
194 ),
195 }
196 }
197
198 /// 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.
199 #[track_caller]
200 #[tracing::instrument(skip(value))]
201 pub fn new_maybe_sync_in_scope(value: T, owner: ScopeId) -> Self {
202 Self::new_maybe_sync_in_scope_with_caller(value, owner, std::panic::Location::caller())
203 }
204
205 /// Create a new signal with a custom owner scope and a custom caller. The signal will be dropped when the owner scope is dropped instead of the current scope.
206 #[tracing::instrument(skip(value))]
207 pub fn new_maybe_sync_in_scope_with_caller(
208 value: T,
209 owner: ScopeId,
210 caller: &'static std::panic::Location<'static>,
211 ) -> Self {
212 Self {
213 inner: CopyValue::<SignalData<T>, S>::new_maybe_sync_in_scope_with_caller(
214 SignalData {
215 subscribers: Default::default(),
216 value,
217 },
218 owner,
219 caller,
220 ),
221 }
222 }
223
224 /// Point to another signal. This will subscribe the other signal to all subscribers of this signal.
225 pub fn point_to(&self, other: Self) -> BorrowResult {
226 #[allow(clippy::mutable_key_type)]
227 let this_subscribers = self.inner.value.read().subscribers.lock().unwrap().clone();
228 let other_read = other.inner.value.read();
229 for subscriber in this_subscribers.iter() {
230 subscriber.subscribe(other_read.subscribers.clone());
231 }
232 self.inner.point_to(other.inner)
233 }
234
235 /// Drop the value out of the signal, invalidating the signal in the process.
236 pub fn manually_drop(&self) {
237 self.inner.manually_drop()
238 }
239
240 /// Get the scope the signal was created in.
241 pub fn origin_scope(&self) -> ScopeId {
242 self.inner.origin_scope()
243 }
244
245 fn update_subscribers(&self) {
246 {
247 let inner = self.inner.read();
248
249 // We cannot hold the subscribers lock while calling mark_dirty, because mark_dirty can run user code which may cause a new subscriber to be added. If we hold the lock, we will deadlock.
250 #[allow(clippy::mutable_key_type)]
251 let mut subscribers = std::mem::take(&mut *inner.subscribers.lock().unwrap());
252 subscribers.retain(|reactive_context| reactive_context.mark_dirty());
253 // Extend the subscribers list instead of overwriting it in case a subscriber is added while reactive contexts are marked dirty
254 inner.subscribers.lock().unwrap().extend(subscribers);
255 }
256 }
257
258 /// Get the generational id of the signal.
259 pub fn id(&self) -> generational_box::GenerationalBoxId {
260 self.inner.id()
261 }
262
263 /// **This pattern is no longer recommended. Prefer [`peek`](Signal::peek) or creating new signals instead.**
264 ///
265 /// This function is the equivalent of the [write_silent](https://docs.rs/dioxus/latest/dioxus/prelude/struct.UseRef.html#method.write_silent) method on use_ref.
266 ///
267 /// ## What you should use instead
268 ///
269 /// ### Reading and Writing to data in the same scope
270 ///
271 /// Reading and writing to the same signal in the same scope will cause that scope to rerun forever:
272 /// ```rust, no_run
273 /// # use dioxus::prelude::*;
274 /// let mut signal = use_signal(|| 0);
275 /// // This makes the scope rerun whenever we write to the signal
276 /// println!("{}", *signal.read());
277 /// // This will rerun the scope because we read the signal earlier in the same scope
278 /// *signal.write() += 1;
279 /// ```
280 ///
281 /// You may have used the write_silent method to avoid this infinite loop with use_ref like this:
282 /// ```rust, no_run
283 /// # use dioxus::prelude::*;
284 /// let signal = use_signal(|| 0);
285 /// // This makes the scope rerun whenever we write to the signal
286 /// println!("{}", *signal.read());
287 /// // Write silent will not rerun any subscribers
288 /// *signal.write_silent() += 1;
289 /// ```
290 ///
291 /// Instead you can use the [`peek`](Signal::peek) and [`write`](Signal::write) methods instead. The peek method will not subscribe to the current scope which will avoid an infinite loop if you are reading and writing to the same signal in the same scope.
292 /// ```rust, no_run
293 /// # use dioxus::prelude::*;
294 /// let mut signal = use_signal(|| 0);
295 /// // Peek will read the value but not subscribe to the current scope
296 /// println!("{}", *signal.peek());
297 /// // Write will update any subscribers which does not include the current scope
298 /// *signal.write() += 1;
299 /// ```
300 ///
301 /// ### Reading and Writing to different data
302 ///
303 ///
304 ///
305 /// ## Why is this pattern no longer recommended?
306 ///
307 /// This pattern is no longer recommended because it is very easy to allow your state and UI to grow out of sync. `write_silent` globally opts out of automatic state updates which can be difficult to reason about.
308 ///
309 ///
310 /// Lets take a look at an example:
311 /// main.rs:
312 /// ```rust, no_run
313 /// # use dioxus::prelude::*;
314 /// # fn Child() -> Element { unimplemented!() }
315 /// fn app() -> Element {
316 /// let signal = use_context_provider(|| Signal::new(0));
317 ///
318 /// // We want to log the value of the signal whenever the app component reruns
319 /// println!("{}", *signal.read());
320 ///
321 /// rsx! {
322 /// button {
323 /// // If we don't want to rerun the app component when the button is clicked, we can use write_silent
324 /// onclick: move |_| *signal.write_silent() += 1,
325 /// "Increment"
326 /// }
327 /// Child {}
328 /// }
329 /// }
330 /// ```
331 /// child.rs:
332 /// ```rust, no_run
333 /// # use dioxus::prelude::*;
334 /// fn Child() -> Element {
335 /// let signal: Signal<i32> = use_context();
336 ///
337 /// // It is difficult to tell that changing the button to use write_silent in the main.rs file will cause UI to be out of sync in a completely different file
338 /// rsx! {
339 /// "{signal}"
340 /// }
341 /// }
342 /// ```
343 ///
344 /// Instead [`peek`](Signal::peek) locally opts out of automatic state updates explicitly for a specific read which is easier to reason about.
345 ///
346 /// Here is the same example using peek:
347 /// main.rs:
348 /// ```rust, no_run
349 /// # use dioxus::prelude::*;
350 /// # fn Child() -> Element { unimplemented!() }
351 /// fn app() -> Element {
352 /// let mut signal = use_context_provider(|| Signal::new(0));
353 ///
354 /// // We want to log the value of the signal whenever the app component reruns, but we don't want to rerun the app component when the signal is updated so we use peek instead of read
355 /// println!("{}", *signal.peek());
356 ///
357 /// rsx! {
358 /// button {
359 /// // We can use write like normal and update the child component automatically
360 /// onclick: move |_| *signal.write() += 1,
361 /// "Increment"
362 /// }
363 /// Child {}
364 /// }
365 /// }
366 /// ```
367 /// child.rs:
368 /// ```rust, no_run
369 /// # use dioxus::prelude::*;
370 /// fn Child() -> Element {
371 /// let signal: Signal<i32> = use_context();
372 ///
373 /// rsx! {
374 /// "{signal}"
375 /// }
376 /// }
377 /// ```
378 #[track_caller]
379 #[deprecated = "This pattern is no longer recommended. Prefer `peek` or creating new signals instead."]
380 pub fn write_silent(&self) -> S::Mut<'static, T> {
381 S::map_mut(self.inner.write_unchecked(), |inner| &mut inner.value)
382 }
383}
384
385impl<T, S: Storage<SignalData<T>>> Readable for Signal<T, S> {
386 type Target = T;
387 type Storage = S;
388
389 #[track_caller]
390 fn try_read_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
391 let inner = self.inner.try_read_unchecked()?;
392
393 if let Some(reactive_context) = ReactiveContext::current() {
394 tracing::trace!("Subscribing to the reactive context {}", reactive_context);
395 reactive_context.subscribe(inner.subscribers.clone());
396 }
397
398 Ok(S::map(inner, |v| &v.value))
399 }
400
401 /// 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.**
402 ///
403 /// If the signal has been dropped, this will panic.
404 #[track_caller]
405 fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
406 self.inner
407 .try_read_unchecked()
408 .map(|inner| S::map(inner, |v| &v.value))
409 }
410}
411
412impl<T: 'static, S: Storage<SignalData<T>>> Writable for Signal<T, S> {
413 type Mut<'a, R: ?Sized + 'static> = Write<'a, R, S>;
414
415 fn map_mut<I: ?Sized, U: ?Sized + 'static, F: FnOnce(&mut I) -> &mut U>(
416 ref_: Self::Mut<'_, I>,
417 f: F,
418 ) -> Self::Mut<'_, U> {
419 Write::map(ref_, f)
420 }
421
422 fn try_map_mut<
423 I: ?Sized + 'static,
424 U: ?Sized + 'static,
425 F: FnOnce(&mut I) -> Option<&mut U>,
426 >(
427 ref_: Self::Mut<'_, I>,
428 f: F,
429 ) -> Option<Self::Mut<'_, U>> {
430 Write::filter_map(ref_, f)
431 }
432
433 fn downcast_lifetime_mut<'a: 'b, 'b, R: ?Sized + 'static>(
434 mut_: Self::Mut<'a, R>,
435 ) -> Self::Mut<'b, R> {
436 Write::downcast_lifetime(mut_)
437 }
438
439 #[track_caller]
440 fn try_write_unchecked(
441 &self,
442 ) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError> {
443 #[cfg(debug_assertions)]
444 let origin = std::panic::Location::caller();
445 self.inner.try_write_unchecked().map(|inner| {
446 let borrow = S::map_mut(inner, |v| &mut v.value);
447 Write {
448 write: borrow,
449 drop_signal: Box::new(SignalSubscriberDrop {
450 signal: *self,
451 #[cfg(debug_assertions)]
452 origin,
453 }),
454 }
455 })
456 }
457}
458
459impl<T> IntoAttributeValue for Signal<T>
460where
461 T: Clone + IntoAttributeValue,
462{
463 fn into_value(self) -> dioxus_core::AttributeValue {
464 self.with(|f| f.clone().into_value())
465 }
466}
467
468impl<T> IntoDynNode for Signal<T>
469where
470 T: Clone + IntoDynNode,
471{
472 fn into_dyn_node(self) -> dioxus_core::DynamicNode {
473 self().into_dyn_node()
474 }
475}
476
477impl<T: 'static, S: Storage<SignalData<T>>> PartialEq for Signal<T, S> {
478 fn eq(&self, other: &Self) -> bool {
479 self.inner == other.inner
480 }
481}
482
483impl<T: 'static, S: Storage<SignalData<T>>> Eq for Signal<T, S> {}
484
485/// Allow calling a signal with signal() syntax
486///
487/// Currently only limited to copy types, though could probably specialize for string/arc/rc
488impl<T: Clone, S: Storage<SignalData<T>> + 'static> Deref for Signal<T, S> {
489 type Target = dyn Fn() -> T;
490
491 fn deref(&self) -> &Self::Target {
492 unsafe { Readable::deref_impl(self) }
493 }
494}
495
496#[cfg(feature = "serialize")]
497impl<T: serde::Serialize + 'static, Store: Storage<SignalData<T>>> serde::Serialize
498 for Signal<T, Store>
499{
500 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
501 self.read().serialize(serializer)
502 }
503}
504
505#[cfg(feature = "serialize")]
506impl<'de, T: serde::Deserialize<'de> + 'static, Store: Storage<SignalData<T>>>
507 serde::Deserialize<'de> for Signal<T, Store>
508{
509 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
510 Ok(Self::new_maybe_sync(T::deserialize(deserializer)?))
511 }
512}
513
514/// A mutable reference to a signal's value. This reference acts similarly to [`std::cell::RefMut`], but it has extra debug information
515/// and integrates with the reactive system to automatically update dependents.
516///
517/// [`Write`] implements [`DerefMut`] which means you can call methods on the inner value just like you would on a mutable reference
518/// to the inner value. If you need to get the inner reference directly, you can call [`Write::deref_mut`].
519///
520/// # Example
521/// ```rust
522/// # use dioxus::prelude::*;
523/// fn app() -> Element {
524/// let mut value = use_signal(|| String::from("hello"));
525///
526/// rsx! {
527/// button {
528/// onclick: move |_| {
529/// let mut mutable_reference = value.write();
530///
531/// // You call methods like `push_str` on the reference just like you would with the inner String
532/// mutable_reference.push_str("world");
533/// },
534/// "Click to add world to the string"
535/// }
536/// div { "{value}" }
537/// }
538/// }
539/// ```
540///
541/// ## Matching on Write
542///
543/// You need to get the inner mutable reference with [`Write::deref_mut`] before you match the inner value. If you try to match
544/// without calling [`Write::deref_mut`], you will get an error like this:
545///
546/// ```compile_fail
547/// # use dioxus::prelude::*;
548/// #[derive(Debug)]
549/// enum Colors {
550/// Red(u32),
551/// Green
552/// }
553/// fn app() -> Element {
554/// let mut value = use_signal(|| Colors::Red(0));
555///
556/// rsx! {
557/// button {
558/// onclick: move |_| {
559/// let mut mutable_reference = value.write();
560///
561/// match mutable_reference {
562/// // Since we are matching on the `Write` type instead of &mut Colors, we can't match on the enum directly
563/// Colors::Red(brightness) => *brightness += 1,
564/// Colors::Green => {}
565/// }
566/// },
567/// "Click to add brightness to the red color"
568/// }
569/// div { "{value:?}" }
570/// }
571/// }
572/// ```
573///
574/// ```text
575/// error[E0308]: mismatched types
576/// --> src/main.rs:18:21
577/// |
578/// 16 | match mutable_reference {
579/// | ----------------- this expression has type `dioxus::prelude::Write<'_, Colors>`
580/// 17 | // Since we are matching on the `Write` t...
581/// 18 | Colors::Red(brightness) => *brightness += 1,
582/// | ^^^^^^^^^^^^^^^^^^^^^^^ expected `Write<'_, Colors>`, found `Colors`
583/// |
584/// = note: expected struct `dioxus::prelude::Write<'_, Colors, >`
585/// found enum `Colors`
586/// ```
587///
588/// Instead, you need to call deref mut on the reference to get the inner value **before** you match on it:
589///
590/// ```rust
591/// use std::ops::DerefMut;
592/// # use dioxus::prelude::*;
593/// #[derive(Debug)]
594/// enum Colors {
595/// Red(u32),
596/// Green
597/// }
598/// fn app() -> Element {
599/// let mut value = use_signal(|| Colors::Red(0));
600///
601/// rsx! {
602/// button {
603/// onclick: move |_| {
604/// let mut mutable_reference = value.write();
605///
606/// // DerefMut converts the `Write` into a `&mut Colors`
607/// match mutable_reference.deref_mut() {
608/// // Now we can match on the inner value
609/// Colors::Red(brightness) => *brightness += 1,
610/// Colors::Green => {}
611/// }
612/// },
613/// "Click to add brightness to the red color"
614/// }
615/// div { "{value:?}" }
616/// }
617/// }
618/// ```
619///
620/// ## Generics
621/// - T is the current type of the write
622/// - S is the storage type of the signal. This type determines if the signal is local to the current thread, or it can be shared across threads.
623pub struct Write<'a, T: ?Sized + 'static, S: AnyStorage = UnsyncStorage> {
624 write: S::Mut<'a, T>,
625 drop_signal: Box<dyn Any>,
626}
627
628impl<'a, T: ?Sized + 'static, S: AnyStorage> Write<'a, T, S> {
629 /// Map the mutable reference to the signal's value to a new type.
630 pub fn map<O: ?Sized>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<'a, O, S> {
631 let Self {
632 write, drop_signal, ..
633 } = myself;
634 Write {
635 write: S::map_mut(write, f),
636 drop_signal,
637 }
638 }
639
640 /// Try to map the mutable reference to the signal's value to a new type
641 pub fn filter_map<O: ?Sized>(
642 myself: Self,
643 f: impl FnOnce(&mut T) -> Option<&mut O>,
644 ) -> Option<Write<'a, O, S>> {
645 let Self {
646 write, drop_signal, ..
647 } = myself;
648 let write = S::try_map_mut(write, f);
649 write.map(|write| Write { write, drop_signal })
650 }
651
652 /// Downcast the lifetime of the mutable reference to the signal's value.
653 ///
654 /// This function enforces the variance of the lifetime parameter `'a` in Mut. Rust will typically infer this cast with a concrete type, but it cannot with a generic type.
655 pub fn downcast_lifetime<'b>(mut_: Self) -> Write<'b, T, S>
656 where
657 'a: 'b,
658 {
659 Write {
660 write: S::downcast_lifetime_mut(mut_.write),
661 drop_signal: mut_.drop_signal,
662 }
663 }
664}
665
666impl<T: ?Sized + 'static, S: AnyStorage> Deref for Write<'_, T, S> {
667 type Target = T;
668
669 fn deref(&self) -> &Self::Target {
670 &self.write
671 }
672}
673
674impl<T: ?Sized, S: AnyStorage> DerefMut for Write<'_, T, S> {
675 fn deref_mut(&mut self) -> &mut Self::Target {
676 &mut self.write
677 }
678}
679
680struct SignalSubscriberDrop<T: 'static, S: Storage<SignalData<T>>> {
681 signal: Signal<T, S>,
682 #[cfg(debug_assertions)]
683 origin: &'static std::panic::Location<'static>,
684}
685
686#[allow(clippy::no_effect)]
687impl<T: 'static, S: Storage<SignalData<T>>> Drop for SignalSubscriberDrop<T, S> {
688 fn drop(&mut self) {
689 #[cfg(debug_assertions)]
690 {
691 tracing::trace!(
692 "Write on signal at {} finished, updating subscribers",
693 self.origin
694 );
695 crate::warnings::signal_write_in_component_body(self.origin);
696 crate::warnings::signal_read_and_write_in_reactive_scope::<T, S>(
697 self.origin,
698 self.signal,
699 );
700 }
701 self.signal.update_subscribers();
702 }
703}
704
705fmt_impls!(Signal<T, S: Storage<SignalData<T>>>);
706default_impl!(Signal<T, S: Storage<SignalData<T>>>);
707write_impls!(Signal<T, S: Storage<SignalData<T>>>);
708
709impl<T: 'static, S: Storage<SignalData<T>>> Clone for Signal<T, S> {
710 fn clone(&self) -> Self {
711 *self
712 }
713}
714
715impl<T: 'static, S: Storage<SignalData<T>>> Copy for Signal<T, S> {}