dioxus_signals/read.rs
1use std::{mem::MaybeUninit, ops::Index};
2
3use dioxus_core::Subscribers;
4use generational_box::{AnyStorage, UnsyncStorage};
5
6use crate::{MappedSignal, ReadSignal};
7
8/// A reference to a value that can be read from.
9#[allow(type_alias_bounds)]
10pub type ReadableRef<'a, T: Readable, O = <T as Readable>::Target> =
11 <T::Storage as AnyStorage>::Ref<'a, O>;
12
13/// A trait for states that can be read from like [`crate::Signal`], [`crate::GlobalSignal`], or [`crate::ReadSignal`]. You may choose to accept this trait as a parameter instead of the concrete type to allow for more flexibility in your API. For example, instead of creating two functions, one that accepts a [`crate::Signal`] and one that accepts a [`crate::GlobalSignal`], you can create one function that accepts a [`Readable`] type.
14///
15/// # Example
16/// ```rust
17/// # use dioxus::prelude::*;
18/// fn double(something_readable: &impl Readable<Target = i32>) -> i32 {
19/// something_readable.cloned() * 2
20/// }
21///
22/// static COUNT: GlobalSignal<i32> = Signal::global(|| 0);
23///
24/// fn MyComponent(count: Signal<i32>) -> Element {
25/// // Since we defined the function in terms of the readable trait, we can use it with any readable type (Signal, GlobalSignal, ReadSignal, etc)
26/// let doubled = use_memo(move || double(&count));
27/// let global_count_doubled = use_memo(|| double(&COUNT));
28/// rsx! {
29/// div {
30/// "Count local: {count}"
31/// "Doubled local: {doubled}"
32/// "Count global: {COUNT}"
33/// "Doubled global: {global_count_doubled}"
34/// }
35/// }
36/// }
37/// ```
38pub trait Readable {
39 /// The target type of the reference.
40 type Target: ?Sized;
41
42 /// The type of the storage this readable uses.
43 type Storage: AnyStorage;
44
45 /// Try to get a reference to the value without checking the lifetime. This will subscribe the current scope to the signal.
46 ///
47 /// NOTE: This method is completely safe because borrow checking is done at runtime.
48 fn try_read_unchecked(
49 &self,
50 ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
51 where
52 Self::Target: 'static;
53
54 /// Try to peek the current value of the signal without subscribing to updates. If the value has
55 /// been dropped, this will return an error.
56 ///
57 /// NOTE: This method is completely safe because borrow checking is done at runtime.
58 fn try_peek_unchecked(
59 &self,
60 ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
61 where
62 Self::Target: 'static;
63
64 /// Get the underlying subscriber list for this readable. This is used to track when the value changes and notify subscribers.
65 fn subscribers(&self) -> Subscribers
66 where
67 Self::Target: 'static;
68}
69
70/// An extension trait for `Readable` types that provides some convenience methods.
71pub trait ReadableExt: Readable {
72 /// Get the current value of the state. If this is a signal, this will subscribe the current scope to the signal.
73 /// If the value has been dropped, this will panic. Calling this on a Signal is the same as
74 /// using the signal() syntax to read and subscribe to its value
75 #[track_caller]
76 fn read(&self) -> ReadableRef<'_, Self>
77 where
78 Self::Target: 'static,
79 {
80 self.try_read().unwrap()
81 }
82
83 /// Try to get the current value of the state. If this is a signal, this will subscribe the current scope to the signal.
84 #[track_caller]
85 fn try_read(&self) -> Result<ReadableRef<'_, Self>, generational_box::BorrowError>
86 where
87 Self::Target: 'static,
88 {
89 self.try_read_unchecked()
90 .map(Self::Storage::downcast_lifetime_ref)
91 }
92
93 /// Get a reference to the value without checking the lifetime. This will subscribe the current scope to the signal.
94 ///
95 /// NOTE: This method is completely safe because borrow checking is done at runtime.
96 #[track_caller]
97 fn read_unchecked(&self) -> ReadableRef<'static, Self>
98 where
99 Self::Target: 'static,
100 {
101 self.try_read_unchecked().unwrap()
102 }
103
104 /// Get the current value of the state without subscribing to updates. If the value has been dropped, this will panic.
105 ///
106 /// # Example
107 /// ```rust
108 /// # use dioxus::prelude::*;
109 /// fn MyComponent(mut count: Signal<i32>) -> Element {
110 /// let mut event_source = use_signal(|| None);
111 /// let doubled = use_memo(move || {
112 /// // We want to log the value of the event_source, but we don't need to rerun the doubled value if the event_source changes (because the value of doubled doesn't depend on the event_source)
113 /// // We can read the value with peek without subscribing to updates
114 /// let source = event_source.peek();
115 /// tracing::info!("Clicked: {source:?}");
116 /// count() * 2
117 /// });
118 /// rsx! {
119 /// div { "Count: {count}" }
120 /// div { "Doubled: {doubled}" }
121 /// button {
122 /// onclick: move |_| {
123 /// event_source.set(Some("Click me button"));
124 /// count += 1;
125 /// },
126 /// "Click me"
127 /// }
128 /// button {
129 /// onclick: move |_| {
130 /// event_source.set(Some("Double me button"));
131 /// count += 1;
132 /// },
133 /// "Double me"
134 /// }
135 /// }
136 /// }
137 /// ```
138 #[track_caller]
139 fn peek(&self) -> ReadableRef<'_, Self>
140 where
141 Self::Target: 'static,
142 {
143 Self::Storage::downcast_lifetime_ref(self.peek_unchecked())
144 }
145
146 /// Try to peek the current value of the signal without subscribing to updates. If the value has
147 /// been dropped, this will return an error.
148 #[track_caller]
149 fn try_peek(&self) -> Result<ReadableRef<'_, Self>, generational_box::BorrowError>
150 where
151 Self::Target: 'static,
152 {
153 self.try_peek_unchecked()
154 .map(Self::Storage::downcast_lifetime_ref)
155 }
156
157 /// Get the current value of the signal without checking the lifetime. **Unlike read, this will not subscribe the current scope to the signal which can cause parts of your UI to not update.**
158 ///
159 /// If the signal has been dropped, this will panic.
160 #[track_caller]
161 fn peek_unchecked(&self) -> ReadableRef<'static, Self>
162 where
163 Self::Target: 'static,
164 {
165 self.try_peek_unchecked().unwrap()
166 }
167
168 /// Map the references of the readable value to a new type. This lets you provide a view
169 /// into the readable value without creating a new signal or cloning the value.
170 ///
171 /// Anything that subscribes to the readable value will be rerun whenever the original value changes, even if the view does not
172 /// change. If you want to memorize the view, you can use a [`crate::Memo`] instead. For fine grained scoped updates, use
173 /// stores instead
174 ///
175 /// # Example
176 /// ```rust
177 /// # use dioxus::prelude::*;
178 /// fn List(list: Signal<Vec<i32>>) -> Element {
179 /// rsx! {
180 /// for index in 0..list.len() {
181 /// // We can use the `map` method to provide a view into the single item in the list that the child component will render
182 /// Item { item: list.map(move |v| &v[index]) }
183 /// }
184 /// }
185 /// }
186 ///
187 /// // The child component doesn't need to know that the mapped value is coming from a list
188 /// #[component]
189 /// fn Item(item: ReadSignal<i32>) -> Element {
190 /// rsx! {
191 /// div { "Item: {item}" }
192 /// }
193 /// }
194 /// ```
195 fn map<F, O>(self, f: F) -> MappedSignal<O, Self, F>
196 where
197 Self: Clone + Sized,
198 F: Fn(&Self::Target) -> &O,
199 {
200 MappedSignal::new(self, f)
201 }
202
203 /// Clone the inner value and return it. If the value has been dropped, this will panic.
204 #[track_caller]
205 fn cloned(&self) -> Self::Target
206 where
207 Self::Target: Clone + 'static,
208 {
209 self.read().clone()
210 }
211
212 /// Run a function with a reference to the value. If the value has been dropped, this will panic.
213 #[track_caller]
214 fn with<O>(&self, f: impl FnOnce(&Self::Target) -> O) -> O
215 where
216 Self::Target: 'static,
217 {
218 f(&*self.read())
219 }
220
221 /// Run a function with a reference to the value. If the value has been dropped, this will panic.
222 #[track_caller]
223 fn with_peek<O>(&self, f: impl FnOnce(&Self::Target) -> O) -> O
224 where
225 Self::Target: 'static,
226 {
227 f(&*self.peek())
228 }
229
230 /// Index into the inner value and return a reference to the result. If the value has been dropped or the index is invalid, this will panic.
231 #[track_caller]
232 fn index<I>(
233 &self,
234 index: I,
235 ) -> ReadableRef<'_, Self, <Self::Target as std::ops::Index<I>>::Output>
236 where
237 Self::Target: std::ops::Index<I> + 'static,
238 {
239 <Self::Storage as AnyStorage>::map(self.read(), |v| v.index(index))
240 }
241
242 /// SAFETY: You must call this function directly with `self` as the argument.
243 /// This function relies on the size of the object you return from the deref
244 /// being the same as the object you pass in
245 #[doc(hidden)]
246 unsafe fn deref_impl<'a>(&self) -> &'a dyn Fn() -> Self::Target
247 where
248 Self: Sized + 'a,
249 Self::Target: Clone + 'static,
250 {
251 // https://github.com/dtolnay/case-studies/tree/master/callable-types
252
253 // First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit<Self>).
254 let uninit_callable = MaybeUninit::<Self>::uninit();
255 // Then move that value into the closure. We assume that the closure now has a in memory layout of Self.
256 let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }).clone();
257
258 // Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure.
259 let size_of_closure = std::mem::size_of_val(&uninit_closure);
260 assert_eq!(size_of_closure, std::mem::size_of::<Self>());
261
262 // Then cast the lifetime of the closure to the lifetime of &self.
263 fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T {
264 b
265 }
266 let reference_to_closure = cast_lifetime(
267 {
268 // The real closure that we will never use.
269 &uninit_closure
270 },
271 #[allow(clippy::missing_transmute_annotations)]
272 // We transmute self into a reference to the closure. This is safe because we know that the closure has the same memory layout as Self so &Closure == &Self.
273 unsafe {
274 std::mem::transmute(self)
275 },
276 );
277
278 // Cast the closure to a trait object.
279 reference_to_closure as &_
280 }
281}
282
283impl<R: Readable + ?Sized> ReadableExt for R {}
284
285/// An extension trait for `Readable` types that can be boxed into a trait object.
286pub trait ReadableBoxExt: Readable<Storage = UnsyncStorage> {
287 /// Box the readable value into a trait object. This is useful for passing around readable values without knowing their concrete type.
288 fn boxed(self) -> ReadSignal<Self::Target>
289 where
290 Self: Sized + 'static,
291 {
292 ReadSignal::new(self)
293 }
294}
295impl<R: Readable<Storage = UnsyncStorage> + ?Sized> ReadableBoxExt for R {}
296
297/// An extension trait for `Readable<Vec<T>>` that provides some convenience methods.
298pub trait ReadableVecExt<T>: Readable<Target = Vec<T>> {
299 /// Returns the length of the inner vector.
300 #[track_caller]
301 fn len(&self) -> usize
302 where
303 T: 'static,
304 {
305 self.with(|v| v.len())
306 }
307
308 /// Returns true if the inner vector is empty.
309 #[track_caller]
310 fn is_empty(&self) -> bool
311 where
312 T: 'static,
313 {
314 self.with(|v| v.is_empty())
315 }
316
317 /// Get the first element of the inner vector.
318 #[track_caller]
319 fn first(&self) -> Option<ReadableRef<'_, Self, T>>
320 where
321 T: 'static,
322 {
323 <Self::Storage as AnyStorage>::try_map(self.read(), |v| v.first())
324 }
325
326 /// Get the last element of the inner vector.
327 #[track_caller]
328 fn last(&self) -> Option<ReadableRef<'_, Self, T>>
329 where
330 T: 'static,
331 {
332 <Self::Storage as AnyStorage>::try_map(self.read(), |v| v.last())
333 }
334
335 /// Get the element at the given index of the inner vector.
336 #[track_caller]
337 fn get(&self, index: usize) -> Option<ReadableRef<'_, Self, T>>
338 where
339 T: 'static,
340 {
341 <Self::Storage as AnyStorage>::try_map(self.read(), |v| v.get(index))
342 }
343
344 /// Get an iterator over the values of the inner vector.
345 #[track_caller]
346 fn iter(&self) -> ReadableValueIterator<'_, Self>
347 where
348 Self: Sized,
349 {
350 ReadableValueIterator {
351 index: 0,
352 value: self,
353 }
354 }
355}
356
357/// An iterator over the values of a `Readable<Vec<T>>`.
358pub struct ReadableValueIterator<'a, R> {
359 index: usize,
360 value: &'a R,
361}
362
363impl<'a, T: 'static, R: Readable<Target = Vec<T>>> Iterator for ReadableValueIterator<'a, R> {
364 type Item = ReadableRef<'a, R, T>;
365
366 fn next(&mut self) -> Option<Self::Item> {
367 let index = self.index;
368 self.index += 1;
369 self.value.get(index)
370 }
371}
372
373impl<T, R> ReadableVecExt<T> for R where R: Readable<Target = Vec<T>> {}
374
375/// An extension trait for `Readable<Option<T>>` that provides some convenience methods.
376pub trait ReadableOptionExt<T>: Readable<Target = Option<T>> {
377 /// Unwraps the inner value and clones it.
378 #[track_caller]
379 fn unwrap(&self) -> T
380 where
381 T: Clone + 'static,
382 {
383 self.as_ref().unwrap().clone()
384 }
385
386 /// Attempts to read the inner value of the Option.
387 #[track_caller]
388 fn as_ref(&self) -> Option<ReadableRef<'_, Self, T>>
389 where
390 T: 'static,
391 {
392 <Self::Storage as AnyStorage>::try_map(self.read(), |v| v.as_ref())
393 }
394}
395
396impl<T, R> ReadableOptionExt<T> for R where R: Readable<Target = Option<T>> {}
397
398/// An extension trait for `Readable<Option<T>>` that provides some convenience methods.
399pub trait ReadableResultExt<T, E>: Readable<Target = Result<T, E>> {
400 /// Unwraps the inner value and clones it.
401 #[track_caller]
402 fn unwrap(&self) -> T
403 where
404 T: Clone + 'static,
405 E: 'static,
406 {
407 self.as_ref()
408 .unwrap_or_else(|_| panic!("Tried to unwrap a Result that was an error"))
409 .clone()
410 }
411
412 /// Attempts to read the inner value of the Option.
413 #[track_caller]
414 fn as_ref(&self) -> Result<ReadableRef<'_, Self, T>, ReadableRef<'_, Self, E>>
415 where
416 T: 'static,
417 E: 'static,
418 {
419 <Self::Storage as AnyStorage>::try_map(self.read(), |v| v.as_ref().ok()).ok_or(
420 <Self::Storage as AnyStorage>::map(self.read(), |v| v.as_ref().err().unwrap()),
421 )
422 }
423}
424
425impl<T, E, R> ReadableResultExt<T, E> for R where R: Readable<Target = Result<T, E>> {}