sycamore_reactive/signals.rs
1//! Reactive signals.
2
3use std::cell::{Ref, RefMut};
4use std::fmt;
5use std::fmt::Formatter;
6use std::hash::Hash;
7use std::marker::PhantomData;
8use std::ops::{AddAssign, Deref, DivAssign, MulAssign, RemAssign, SubAssign};
9
10use slotmap::Key;
11use smallvec::SmallVec;
12
13use crate::*;
14
15/// A read-only reactive value.
16///
17/// Unlike the difference between Rust's shared and mutable-references (`&T` and `&mut`), the
18/// underlying data is not immutable. The data can be updated with the corresponding [`Signal`]
19/// (which has mutable access) and will show up in the `ReadSignal` as well.
20///
21/// A `ReadSignal` can be simply obtained by dereferencing a [`Signal`]. In fact, every [`Signal`]
22/// is a `ReadSignal` with additional write abilities!
23///
24/// # Example
25/// ```
26/// # use sycamore_reactive::*;
27/// # create_root(|| {
28/// let signal: Signal<i32> = create_signal(123);
29/// let read_signal: ReadSignal<i32> = *signal;
30/// assert_eq!(read_signal.get(), 123);
31/// signal.set(456);
32/// assert_eq!(read_signal.get(), 456);
33/// // read_signal.set(789); // <-- This is not allowed!
34/// # });
35/// ```
36///
37/// See [`create_signal`] for more information.
38pub struct ReadSignal<T: 'static> {
39 pub(crate) id: NodeId,
40 root: &'static Root,
41 /// Keep track of where the signal was created for diagnostics.
42 /// This is also stored in the Node but we want to have access to this when accessing a
43 /// disposed node so we store it here as well.
44 #[cfg(debug_assertions)]
45 created_at: &'static std::panic::Location<'static>,
46 _phantom: PhantomData<T>,
47}
48
49/// A reactive value that can be read and written to.
50///
51/// This is the writable version of [`ReadSignal`].
52///
53/// See [`create_signal`] for more information.
54pub struct Signal<T: 'static>(pub(crate) ReadSignal<T>);
55
56/// Create a new [`Signal`].
57///
58/// Signals are reactive atoms, pieces of state that can be read and written to and which will
59/// automatically update anything which depend on them.
60///
61/// # Usage
62/// The simplest way to use a signal is by using [`.get()`](ReadSignal::get) and
63/// [`.set(...)`](Signal::set). However, this only works if the value implements [`Copy`]. If
64/// we wanted to store something that doesn't implement [`Copy`] but implements [`Clone`] instead,
65/// say a [`String`], we can use [`.get_clone()`](ReadSignal::get_clone) which will automatically
66/// clone the value for us.
67///
68/// ```rust
69/// # use sycamore_reactive::*;
70/// # create_root(|| {
71/// let signal = create_signal(1);
72/// signal.get(); // Should return 1.
73/// signal.set(2);
74/// signal.get(); // Should return 2.
75/// # });
76/// ```
77///
78/// There are many other ways of getting and setting signals, such as
79/// [`.with(...)`](ReadSignal::with) and [`.update(...)`](Signal::update) which can access the
80/// signal even if it does not implement [`Clone`] or if you simply don't want to pay the
81/// performance overhead of cloning your value everytime you read it.
82///
83/// # Reactivity
84/// What makes signals so powerful, as opposed to some other wrapper type like
85/// [`RefCell`](std::cell::RefCell) is the automatic dependency tracking. This means that accessing
86/// a signal will automatically add it as a dependency in certain contexts (such as inside a
87/// [`create_memo`](crate::create_memo)) which allows us to update related state whenever the signal
88/// is changed.
89///
90/// ```rust
91/// # use sycamore_reactive::*;
92/// # create_root(|| {
93/// let signal = create_signal(1);
94/// // Note that we are accessing signal inside a closure in the line below. This will cause it to
95/// // be automatically tracked and update our double value whenever signal is changed.
96/// let double = create_memo(move || signal.get() * 2);
97/// double.get(); // Should return 2.
98/// signal.set(2);
99/// double.get(); // Should return 4. Notice how this value was updated automatically when we
100/// // modified signal. This way, we can rest assured that all our state will be
101/// // consistent at all times!
102/// # });
103/// ```
104///
105/// # Ownership
106/// Signals are always associated with a reactive node. This is what performs the memory management
107/// for the actual value of the signal. What is returned from this function is just a
108/// handle/reference to the signal allocted in the reactive node. This allows us to freely copy this
109/// handle around and use it in closures and event handlers without worrying about ownership of the
110/// signal.
111///
112/// This is why in the above example, we could access `signal` even after it was moved in to the
113/// closure of the `create_memo`.
114#[cfg_attr(debug_assertions, track_caller)]
115pub fn create_signal<T>(value: T) -> Signal<T> {
116 let signal = create_empty_signal();
117 signal.get_mut().value = Some(Box::new(value));
118 signal
119}
120
121/// Creates a new [`Signal`] with the `value` field set to `None`.
122#[cfg_attr(debug_assertions, track_caller)]
123pub(crate) fn create_empty_signal<T>() -> Signal<T> {
124 let root = Root::global();
125 let id = root.nodes.borrow_mut().insert(ReactiveNode {
126 value: None,
127 callback: None,
128 children: Vec::new(),
129 parent: root.current_node.get(),
130 dependents: Vec::new(),
131 dependencies: SmallVec::new(),
132 cleanups: Vec::new(),
133 context: Vec::new(),
134 state: NodeState::Clean,
135 mark: Mark::None,
136 #[cfg(debug_assertions)]
137 created_at: std::panic::Location::caller(),
138 });
139 // Add the signal to the parent's `children` list.
140 let current_node = root.current_node.get();
141 if !current_node.is_null() {
142 root.nodes.borrow_mut()[current_node].children.push(id);
143 }
144
145 Signal(ReadSignal {
146 id,
147 root,
148 #[cfg(debug_assertions)]
149 created_at: std::panic::Location::caller(),
150 _phantom: PhantomData,
151 })
152}
153
154impl<T> ReadSignal<T> {
155 /// Get a immutable reference to the underlying node.
156 #[cfg_attr(debug_assertions, track_caller)]
157 pub(crate) fn get_ref(self) -> Ref<'static, ReactiveNode> {
158 Ref::map(self.root.nodes.borrow(), |nodes| match nodes.get(self.id) {
159 Some(node) => node,
160 None => panic!("{}", self.get_disposed_panic_message()),
161 })
162 }
163
164 /// Get a mutable reference to the underlying node.
165 #[cfg_attr(debug_assertions, track_caller)]
166 pub(crate) fn get_mut(self) -> RefMut<'static, ReactiveNode> {
167 RefMut::map(self.root.nodes.borrow_mut(), |nodes| {
168 match nodes.get_mut(self.id) {
169 Some(node) => node,
170 None => panic!("{}", self.get_disposed_panic_message()),
171 }
172 })
173 }
174
175 /// Returns `true` if the signal is still alive, i.e. has not yet been disposed.
176 pub fn is_alive(self) -> bool {
177 self.root.nodes.borrow().get(self.id).is_some()
178 }
179
180 /// Disposes the signal, i.e. frees up the memory held on by this signal. Accessing a signal
181 /// after it has been disposed immediately causes a panic.
182 pub fn dispose(self) {
183 NodeHandle(self.id, self.root).dispose();
184 }
185
186 fn get_disposed_panic_message(self) -> String {
187 #[cfg(not(debug_assertions))]
188 return "signal was disposed".to_string();
189
190 #[cfg(debug_assertions)]
191 return format!("signal was disposed. Created at {}", self.created_at);
192 }
193
194 /// Get the value of the signal without tracking it. The type must implement [`Copy`]. If this
195 /// is not the case, use [`ReadSignal::get_clone_untracked`] or [`ReadSignal::with_untracked`]
196 /// instead.
197 ///
198 /// # Example
199 /// ```
200 /// # use sycamore_reactive::*;
201 /// # create_root(|| {
202 /// let state = create_signal(0);
203 /// // Note that we have used `get_untracked` here so the signal is not actually being tracked
204 /// // by the memo.
205 /// let doubled = create_memo(move || state.get_untracked() * 2);
206 /// state.set(1);
207 /// assert_eq!(doubled.get(), 0);
208 /// # });
209 /// ```
210 #[cfg_attr(debug_assertions, track_caller)]
211 pub fn get_untracked(self) -> T
212 where
213 T: Copy,
214 {
215 self.with_untracked(|value| *value)
216 }
217
218 /// Get the value of the signal without tracking it. The type is [`Clone`]-ed automatically.
219 ///
220 /// This is the cloned equivalent of [`ReadSignal::get_untracked`].
221 #[cfg_attr(debug_assertions, track_caller)]
222 pub fn get_clone_untracked(self) -> T
223 where
224 T: Clone,
225 {
226 self.with_untracked(Clone::clone)
227 }
228
229 /// Get the value of the signal. The type must implement [`Copy`]. If this is not the case, use
230 /// [`ReadSignal::get_clone_untracked`] or [`ReadSignal::with_untracked`] instead.
231 ///
232 /// When called inside a reactive scope, the signal will be automatically tracked.
233 ///
234 /// # Example
235 /// ```
236 /// # use sycamore_reactive::*;
237 /// # create_root(|| {
238 /// let state = create_signal(0);
239 /// assert_eq!(state.get(), 0);
240 ///
241 /// state.set(1);
242 /// assert_eq!(state.get(), 1);
243 ///
244 /// // The signal is automatically tracked in the line below.
245 /// let doubled = create_memo(move || state.get());
246 /// # });
247 /// ```
248 #[cfg_attr(debug_assertions, track_caller)]
249 pub fn get(self) -> T
250 where
251 T: Copy,
252 {
253 self.track();
254 self.get_untracked()
255 }
256
257 /// Get the value of the signal. The type is [`Clone`]-ed automatically.
258 ///
259 /// When called inside a reactive scope, the signal will be automatically tracked.
260 ///
261 /// If the value implements [`Copy`], you should use [`ReadSignal::get`] instead.
262 ///
263 /// # Example
264 /// ```
265 /// # use sycamore_reactive::*;
266 /// # create_root(|| {
267 /// let greeting = create_signal("Hello".to_string());
268 /// assert_eq!(greeting.get_clone(), "Hello".to_string());
269 ///
270 /// // The signal is automatically tracked in the line below.
271 /// let hello_world = create_memo(move || format!("{} World!", greeting.get_clone()));
272 /// assert_eq!(hello_world.get_clone(), "Hello World!");
273 ///
274 /// greeting.set("Goodbye".to_string());
275 /// assert_eq!(greeting.get_clone(), "Goodbye".to_string());
276 /// assert_eq!(hello_world.get_clone(), "Goodbye World!");
277 /// # });
278 /// ```
279 #[cfg_attr(debug_assertions, track_caller)]
280 pub fn get_clone(self) -> T
281 where
282 T: Clone,
283 {
284 self.track();
285 self.get_clone_untracked()
286 }
287
288 /// Get a value from the signal without tracking it.
289 #[cfg_attr(debug_assertions, track_caller)]
290 pub fn with_untracked<U>(self, f: impl FnOnce(&T) -> U) -> U {
291 let node = self.get_ref();
292 let value = node.value.as_ref().expect("value updating");
293 let ret = f(value.downcast_ref().expect("wrong signal type"));
294 ret
295 }
296
297 /// Get a value from the signal.
298 ///
299 /// When called inside a reactive scope, the signal will be automatically tracked.
300 #[cfg_attr(debug_assertions, track_caller)]
301 pub fn with<U>(self, f: impl FnOnce(&T) -> U) -> U {
302 self.track();
303 self.with_untracked(f)
304 }
305
306 /// Creates a new [memo](create_memo) from this signal and a function. The resulting memo will
307 /// be created in the current reactive scope.
308 ///
309 /// # Example
310 /// ```
311 /// # use sycamore_reactive::*;
312 /// # create_root(|| {
313 /// let state = create_signal(0);
314 /// let doubled = state.map(|val| *val * 2);
315 /// assert_eq!(doubled.get(), 0);
316 /// state.set(1);
317 /// assert_eq!(doubled.get(), 2);
318 /// # });
319 /// ```
320 #[cfg_attr(debug_assertions, track_caller)]
321 pub fn map<U>(self, mut f: impl FnMut(&T) -> U + 'static) -> ReadSignal<U> {
322 create_memo(move || self.with(&mut f))
323 }
324
325 /// Track the signal in the current reactive scope. This is done automatically when calling
326 /// [`ReadSignal::get`] and other similar methods.
327 ///
328 /// # Example
329 /// ```
330 /// # use sycamore_reactive::*;
331 /// # create_root(|| {
332 /// let state = create_signal(0);
333 /// create_effect(move || {
334 /// state.track(); // Track the signal without getting its value.
335 /// println!("Yipee!");
336 /// });
337 /// state.set(1); // Prints "Yipee!"
338 /// # });
339 /// ```
340 pub fn track(self) {
341 if let Some(tracker) = &mut *self.root.tracker.borrow_mut() {
342 tracker.dependencies.push(self.id);
343 }
344 }
345}
346
347impl<T> Signal<T> {
348 /// Silently set a new value for the signal. This will not trigger any updates in dependent
349 /// signals. As such, this is generally not recommended as it can easily lead to state
350 /// inconsistencies.
351 ///
352 /// # Example
353 /// ```
354 /// # use sycamore_reactive::*;
355 /// # create_root(|| {
356 /// let state = create_signal(0);
357 /// let doubled = create_memo(move || state.get() * 2);
358 /// assert_eq!(doubled.get(), 0);
359 /// state.set_silent(1);
360 /// assert_eq!(doubled.get(), 0); // We now have inconsistent state!
361 /// # });
362 /// ```
363 #[cfg_attr(debug_assertions, track_caller)]
364 pub fn set_silent(self, new: T) {
365 self.replace_silent(new);
366 }
367
368 /// Set a new value for the signal and automatically update any dependents.
369 ///
370 /// # Example
371 /// ```
372 /// # use sycamore_reactive::*;
373 /// # create_root(|| {
374 /// let state = create_signal(0);
375 /// let doubled = create_memo(move || state.get() * 2);
376 /// assert_eq!(doubled.get(), 0);
377 /// state.set(1);
378 /// assert_eq!(doubled.get(), 2);
379 /// # });
380 /// ```
381 #[cfg_attr(debug_assertions, track_caller)]
382 pub fn set(self, new: T) {
383 self.replace(new);
384 }
385
386 /// Silently set a new value for the signal and return the previous value.
387 ///
388 /// This is the silent version of [`Signal::replace`].
389 #[cfg_attr(debug_assertions, track_caller)]
390 pub fn replace_silent(self, new: T) -> T {
391 self.update_silent(|val| std::mem::replace(val, new))
392 }
393
394 /// Set a new value for the signal and return the previous value.
395 ///
396 /// # Example
397 /// ```
398 /// # use sycamore_reactive::*;
399 /// # create_root(|| {
400 /// let state = create_signal(123);
401 /// let prev = state.replace(456);
402 /// assert_eq!(state.get(), 456);
403 /// assert_eq!(prev, 123);
404 /// # });
405 /// ```
406 #[cfg_attr(debug_assertions, track_caller)]
407 pub fn replace(self, new: T) -> T {
408 self.update(|val| std::mem::replace(val, new))
409 }
410
411 /// Silently gets the value of the signal and sets the new value to the default value.
412 ///
413 /// This is the silent version of [`Signal::take`].
414 #[cfg_attr(debug_assertions, track_caller)]
415 pub fn take_silent(self) -> T
416 where
417 T: Default,
418 {
419 self.replace_silent(T::default())
420 }
421
422 /// Gets the value of the signal and sets the new value to the default value.
423 ///
424 /// # Example
425 /// ```
426 /// # use sycamore_reactive::*;
427 /// # create_root(|| {
428 /// let state = create_signal(Some(123));
429 /// let prev = state.take();
430 /// assert_eq!(state.get(), None);
431 /// assert_eq!(prev, Some(123));
432 /// # });
433 /// ```
434 #[cfg_attr(debug_assertions, track_caller)]
435 pub fn take(self) -> T
436 where
437 T: Default,
438 {
439 self.replace(T::default())
440 }
441
442 /// Update the value of the signal silently. This will not trigger any updates in dependent
443 /// signals. As such, this is generally not recommended as it can easily lead to state
444 /// inconsistencies.
445 ///
446 /// This is the silent version of [`Signal::update`].
447 #[cfg_attr(debug_assertions, track_caller)]
448 pub fn update_silent<U>(self, f: impl FnOnce(&mut T) -> U) -> U {
449 let mut value = self.get_mut().value.take().expect("value updating");
450 let ret = f(value.downcast_mut().expect("wrong signal type"));
451 self.get_mut().value = Some(value);
452 ret
453 }
454
455 /// Update the value of the signal and automatically update any dependents.
456 ///
457 /// Using this has the advantage of not needing to clone the value when updating it, especially
458 /// with types that do not implement `Copy` where cloning can be expensive, or for types that
459 /// do not implement `Clone` at all.
460 ///
461 /// # Example
462 /// ```
463 /// # use sycamore_reactive::*;
464 /// # create_root(|| {
465 /// let state = create_signal("Hello".to_string());
466 /// state.update(|val| val.push_str(" Sycamore!"));
467 /// assert_eq!(state.get_clone(), "Hello Sycamore!");
468 /// # });
469 /// ```
470 #[cfg_attr(debug_assertions, track_caller)]
471 pub fn update<U>(self, f: impl FnOnce(&mut T) -> U) -> U {
472 let ret = self.update_silent(f);
473 self.0.root.propagate_updates(self.0.id);
474 ret
475 }
476
477 /// Use a function to produce a new value and sets the value silently.
478 ///
479 /// This is the silent version of [`Signal::set_fn`].
480 #[cfg_attr(debug_assertions, track_caller)]
481 pub fn set_fn_silent(self, f: impl FnOnce(&T) -> T) {
482 self.update_silent(move |val| *val = f(val));
483 }
484
485 /// Use a function to produce a new value and sets the value.
486 ///
487 /// # Example
488 /// ```
489 /// # use sycamore_reactive::*;
490 /// # create_root(|| {
491 /// let state = create_signal(123);
492 /// state.set_fn(|val| *val + 1);
493 /// assert_eq!(state.get(), 124);
494 /// # });
495 /// ```
496 #[cfg_attr(debug_assertions, track_caller)]
497 pub fn set_fn(self, f: impl FnOnce(&T) -> T) {
498 self.update(move |val| *val = f(val));
499 }
500
501 /// Split the signal into a reader/writter pair.
502 ///
503 /// # Example
504 /// ```
505 /// # use sycamore_reactive::*;
506 /// # create_root(|| {
507 /// let (read_signal, mut write_signal) = create_signal(0).split();
508 /// assert_eq!(read_signal.get(), 0);
509 /// write_signal(1);
510 /// assert_eq!(read_signal.get(), 1);
511 /// # });
512 /// ```
513 pub fn split(self) -> (ReadSignal<T>, impl Fn(T) -> T) {
514 (*self, move |value| self.replace(value))
515 }
516}
517
518/// We manually implement `Clone` + `Copy` for `Signal` so that we don't get extra bounds on `T`.
519impl<T> Clone for ReadSignal<T> {
520 fn clone(&self) -> Self {
521 *self
522 }
523}
524impl<T> Copy for ReadSignal<T> {}
525
526impl<T> Clone for Signal<T> {
527 fn clone(&self) -> Self {
528 *self
529 }
530}
531impl<T> Copy for Signal<T> {}
532
533// Implement `Default` for `ReadSignal` and `Signal`.
534impl<T: Default> Default for ReadSignal<T> {
535 fn default() -> Self {
536 *create_signal(Default::default())
537 }
538}
539impl<T: Default> Default for Signal<T> {
540 fn default() -> Self {
541 create_signal(Default::default())
542 }
543}
544
545// Forward `PartialEq`, `Eq`, `PartialOrd`, `Ord`, `Hash` from inner type.
546impl<T: PartialEq> PartialEq for ReadSignal<T> {
547 fn eq(&self, other: &Self) -> bool {
548 self.with(|value| other.with(|other| value == other))
549 }
550}
551impl<T: Eq> Eq for ReadSignal<T> {}
552impl<T: PartialOrd> PartialOrd for ReadSignal<T> {
553 #[cfg_attr(debug_assertions, track_caller)]
554 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
555 self.with(|value| other.with(|other| value.partial_cmp(other)))
556 }
557}
558impl<T: Ord> Ord for ReadSignal<T> {
559 #[cfg_attr(debug_assertions, track_caller)]
560 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
561 self.with(|value| other.with(|other| value.cmp(other)))
562 }
563}
564impl<T: Hash> Hash for ReadSignal<T> {
565 #[cfg_attr(debug_assertions, track_caller)]
566 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
567 self.with(|value| value.hash(state))
568 }
569}
570
571impl<T: PartialEq> PartialEq for Signal<T> {
572 #[cfg_attr(debug_assertions, track_caller)]
573 fn eq(&self, other: &Self) -> bool {
574 self.with(|value| other.with(|other| value == other))
575 }
576}
577impl<T: Eq> Eq for Signal<T> {}
578impl<T: PartialOrd> PartialOrd for Signal<T> {
579 #[cfg_attr(debug_assertions, track_caller)]
580 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
581 self.with(|value| other.with(|other| value.partial_cmp(other)))
582 }
583}
584impl<T: Ord> Ord for Signal<T> {
585 #[cfg_attr(debug_assertions, track_caller)]
586 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
587 self.with(|value| other.with(|other| value.cmp(other)))
588 }
589}
590impl<T: Hash> Hash for Signal<T> {
591 #[cfg_attr(debug_assertions, track_caller)]
592 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
593 self.with(|value| value.hash(state))
594 }
595}
596
597impl<T> Deref for Signal<T> {
598 type Target = ReadSignal<T>;
599
600 fn deref(&self) -> &Self::Target {
601 &self.0
602 }
603}
604
605// Formatting implementations for `ReadSignal` and `Signal`.
606impl<T: fmt::Debug> fmt::Debug for ReadSignal<T> {
607 #[cfg_attr(debug_assertions, track_caller)]
608 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
609 self.with(|value| value.fmt(f))
610 }
611}
612impl<T: fmt::Debug> fmt::Debug for Signal<T> {
613 #[cfg_attr(debug_assertions, track_caller)]
614 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
615 self.with(|value| value.fmt(f))
616 }
617}
618
619impl<T: fmt::Display> fmt::Display for ReadSignal<T> {
620 #[cfg_attr(debug_assertions, track_caller)]
621 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
622 self.with(|value| value.fmt(f))
623 }
624}
625impl<T: fmt::Display> fmt::Display for Signal<T> {
626 #[cfg_attr(debug_assertions, track_caller)]
627 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
628 self.with(|value| value.fmt(f))
629 }
630}
631
632// Serde implementations for `ReadSignal` and `Signal`.
633#[cfg(feature = "serde")]
634impl<T: serde::Serialize> serde::Serialize for ReadSignal<T> {
635 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
636 self.with(|value| value.serialize(serializer))
637 }
638}
639#[cfg(feature = "serde")]
640impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for ReadSignal<T> {
641 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
642 Ok(*create_signal(T::deserialize(deserializer)?))
643 }
644}
645#[cfg(feature = "serde")]
646impl<T: serde::Serialize> serde::Serialize for Signal<T> {
647 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
648 self.with(|value| value.serialize(serializer))
649 }
650}
651#[cfg(feature = "serde")]
652impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for Signal<T> {
653 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
654 Ok(create_signal(T::deserialize(deserializer)?))
655 }
656}
657
658#[cfg(feature = "nightly")]
659impl<T: Copy> FnOnce<()> for ReadSignal<T> {
660 type Output = T;
661
662 extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
663 self.get()
664 }
665}
666
667impl<T: AddAssign<Rhs>, Rhs> AddAssign<Rhs> for Signal<T> {
668 fn add_assign(&mut self, rhs: Rhs) {
669 self.update(|this| *this += rhs);
670 }
671}
672impl<T: SubAssign<Rhs>, Rhs> SubAssign<Rhs> for Signal<T> {
673 fn sub_assign(&mut self, rhs: Rhs) {
674 self.update(|this| *this -= rhs);
675 }
676}
677impl<T: MulAssign<Rhs>, Rhs> MulAssign<Rhs> for Signal<T> {
678 fn mul_assign(&mut self, rhs: Rhs) {
679 self.update(|this| *this *= rhs);
680 }
681}
682impl<T: DivAssign<Rhs>, Rhs> DivAssign<Rhs> for Signal<T> {
683 fn div_assign(&mut self, rhs: Rhs) {
684 self.update(|this| *this /= rhs);
685 }
686}
687impl<T: RemAssign<Rhs>, Rhs> RemAssign<Rhs> for Signal<T> {
688 fn rem_assign(&mut self, rhs: Rhs) {
689 self.update(|this| *this %= rhs);
690 }
691}
692
693// We need to implement this again for `Signal` despite `Signal` deref-ing to `ReadSignal` since
694// we also have another implementation of `FnOnce` for `Signal`.
695#[cfg(feature = "nightly")]
696impl<T: Copy> FnOnce<()> for Signal<T> {
697 type Output = T;
698
699 extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
700 self.get()
701 }
702}
703
704#[cfg(feature = "nightly")]
705impl<T: Copy> FnOnce<(T,)> for Signal<T> {
706 type Output = T;
707
708 extern "rust-call" fn call_once(self, (val,): (T,)) -> Self::Output {
709 self.replace(val)
710 }
711}
712
713#[cfg(test)]
714mod tests {
715 use crate::*;
716
717 #[test]
718 fn signal() {
719 let _ = create_root(|| {
720 let state = create_signal(0);
721 assert_eq!(state.get(), 0);
722
723 state.set(1);
724 assert_eq!(state.get(), 1);
725
726 state.set_fn(|n| *n + 1);
727 assert_eq!(state.get(), 2);
728 });
729 }
730
731 #[test]
732 fn signal_composition() {
733 let _ = create_root(|| {
734 let state = create_signal(0);
735 let double = || state.get() * 2;
736
737 assert_eq!(double(), 0);
738 state.set(1);
739 assert_eq!(double(), 2);
740 });
741 }
742
743 #[test]
744 fn set_silent_signal() {
745 let _ = create_root(|| {
746 let state = create_signal(0);
747 let double = state.map(|&x| x * 2);
748
749 assert_eq!(double.get(), 0);
750 state.set_silent(1);
751 assert_eq!(double.get(), 0); // double value is unchanged.
752
753 state.set_fn_silent(|n| n + 1);
754 assert_eq!(double.get(), 0); // double value is unchanged.
755 });
756 }
757
758 #[test]
759 fn read_signal() {
760 let _ = create_root(|| {
761 let state = create_signal(0);
762 let readonly: ReadSignal<i32> = *state;
763
764 assert_eq!(readonly.get(), 0);
765 state.set(1);
766 assert_eq!(readonly.get(), 1);
767 });
768 }
769
770 #[test]
771 fn map_signal() {
772 let _ = create_root(|| {
773 let state = create_signal(0);
774 let double = state.map(|&x| x * 2);
775
776 assert_eq!(double.get(), 0);
777 state.set(1);
778 assert_eq!(double.get(), 2);
779 });
780 }
781
782 #[test]
783 fn take_signal() {
784 let _ = create_root(|| {
785 let state = create_signal(123);
786
787 let x = state.take();
788 assert_eq!(x, 123);
789 assert_eq!(state.get(), 0);
790 });
791 }
792
793 #[test]
794 fn take_silent_signal() {
795 let _ = create_root(|| {
796 let state = create_signal(123);
797 let double = state.map(|&x| x * 2);
798
799 // Do not trigger subscribers.
800 state.take_silent();
801 assert_eq!(state.get(), 0);
802 assert_eq!(double.get(), 246);
803 });
804 }
805
806 #[test]
807 fn signal_split() {
808 let _ = create_root(|| {
809 let (state, set_state) = create_signal(0).split();
810 assert_eq!(state.get(), 0);
811
812 set_state(1);
813 assert_eq!(state.get(), 1);
814 });
815 }
816
817 #[test]
818 fn signal_display() {
819 let _ = create_root(|| {
820 let signal = create_signal(0);
821 assert_eq!(format!("{signal}"), "0");
822 let read_signal: ReadSignal<_> = *signal;
823 assert_eq!(format!("{read_signal}"), "0");
824 let memo = create_memo(|| 0);
825 assert_eq!(format!("{memo}"), "0");
826 });
827 }
828
829 #[test]
830 fn signal_debug() {
831 let _ = create_root(|| {
832 let signal = create_signal(0);
833 assert_eq!(format!("{signal:?}"), "0");
834 let read_signal: ReadSignal<_> = *signal;
835 assert_eq!(format!("{read_signal:?}"), "0");
836 let memo = create_memo(|| 0);
837 assert_eq!(format!("{memo:?}"), "0");
838 });
839 }
840
841 #[test]
842 fn signal_add_assign_update() {
843 let _ = create_root(|| {
844 let mut signal = create_signal(0);
845 let counter = create_signal(0);
846 create_effect(move || {
847 signal.track();
848 counter.set(counter.get_untracked() + 1);
849 });
850 signal += 1;
851 signal -= 1;
852 signal *= 1;
853 signal /= 1;
854 assert_eq!(counter.get(), 5);
855 });
856 }
857
858 #[test]
859 fn signal_update() {
860 let _ = create_root(|| {
861 let signal = create_signal("Hello ".to_string());
862 let counter = create_signal(0);
863 create_effect(move || {
864 signal.track();
865 counter.set(counter.get_untracked() + 1);
866 });
867 signal.update(|value| value.push_str("World!"));
868 assert_eq!(signal.get_clone(), "Hello World!");
869 assert_eq!(counter.get(), 2);
870 });
871 }
872}