Merge branch 'master' into signal-read-untracked

This commit is contained in:
ealmloff authored and GitHub committed 2023-12-07 07:12:30 -06:00
commit c67e0705ea
99 files changed
+1545 -355

No files matched your search

+58 -18
View File
@@ -1,5 +1,5 @@
use std::{
cell::{Ref, RefCell, RefMut},
cell::RefCell,
mem::MaybeUninit,
ops::{Deref, DerefMut},
rc::Rc,
@@ -10,6 +10,7 @@ use dioxus_core::{
prelude::{current_scope_id, has_context, provide_context, schedule_update_any},
ScopeId, ScopeState,
};
use generational_box::{GenerationalRef, GenerationalRefMut};
use crate::{get_effect_stack, CopyValue, Effect, EffectStack};
@@ -44,9 +45,19 @@ use crate::{get_effect_stack, CopyValue, Effect, EffectStack};
/// }
/// }
/// ```
#[track_caller]
#[must_use]
pub fn use_signal<T: 'static>(cx: &ScopeState, f: impl FnOnce() -> T) -> Signal<T> {
*cx.use_hook(|| Signal::new(f()))
#[cfg(debug_assertions)]
let caller = std::panic::Location::caller();
*cx.use_hook(|| {
Signal::new_with_caller(
f(),
#[cfg(debug_assertions)]
caller,
)
})
}
#[derive(Clone)]
@@ -138,6 +149,7 @@ impl<'de, T: serde::Deserialize<'de> + 'static> serde::Deserialize<'de> for Sign
impl<T: 'static> Signal<T> {
/// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
#[track_caller]
pub fn new(value: T) -> Self {
Self {
inner: CopyValue::new(SignalData {
@@ -150,6 +162,26 @@ impl<T: 'static> Signal<T> {
}
}
/// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
fn new_with_caller(
value: T,
#[cfg(debug_assertions)] caller: &'static std::panic::Location<'static>,
) -> Self {
Self {
inner: CopyValue::new_with_caller(
SignalData {
subscribers: Default::default(),
effect_subscribers: Default::default(),
update_any: schedule_update_any().expect("in a virtual dom"),
value,
effect_stack: get_effect_stack(),
},
#[cfg(debug_assertions)]
caller,
),
}
}
/// 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.
pub fn new_in_scope(value: T, owner: ScopeId) -> Self {
Self {
@@ -174,7 +206,8 @@ impl<T: 'static> Signal<T> {
/// Get the current value of the signal. This will subscribe the current scope to the signal. If you would like to read the signal without subscribing to it, you can use [`Self::peak`] instead.
///
/// If the signal has been dropped, this will panic.
pub fn read(&self) -> Ref<T> {
#[track_caller]
pub fn read(&self) -> GenerationalRef<T> {
let inner = self.inner.read();
if let Some(effect) = inner.effect_stack.current() {
let mut effect_subscribers = inner.effect_subscribers.borrow_mut();
@@ -198,7 +231,7 @@ impl<T: 'static> Signal<T> {
}
}
}
Ref::map(inner, |v| &v.value)
GenerationalRef::map(inner, |v| &v.value)
}
/// 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.**
@@ -212,9 +245,10 @@ impl<T: 'static> Signal<T> {
/// Get a mutable reference to the signal's value.
///
/// If the signal has been dropped, this will panic.
pub fn write(&self) -> Write<'_, T> {
#[track_caller]
pub fn write(&self) -> Write<T> {
let inner = self.inner.write();
let borrow = RefMut::map(inner, |v| &mut v.value);
let borrow = GenerationalRefMut::map(inner, |v| &mut v.value);
Write {
write: borrow,
signal: SignalSubscriberDrop { signal: *self },
@@ -250,12 +284,14 @@ impl<T: 'static> Signal<T> {
}
/// Set the value of the signal. This will trigger an update on all subscribers.
#[track_caller]
pub fn set(&self, value: T) {
*self.write() = value;
}
/// Run a closure with a reference to the signal's value.
/// If the signal has been dropped, this will panic.
#[track_caller]
pub fn with<O>(&self, f: impl FnOnce(&T) -> O) -> O {
let write = self.read();
f(&*write)
@@ -263,6 +299,7 @@ impl<T: 'static> Signal<T> {
/// Run a closure with a mutable reference to the signal's value.
/// If the signal has been dropped, this will panic.
#[track_caller]
pub fn with_mut<O>(&self, f: impl FnOnce(&mut T) -> O) -> O {
let mut write = self.write();
f(&mut *write)
@@ -272,6 +309,7 @@ impl<T: 'static> Signal<T> {
impl<T: Clone + 'static> Signal<T> {
/// Get the current value of the signal. This will subscribe the current scope to the signal.
/// If the signal has been dropped, this will panic.
#[track_caller]
pub fn value(&self) -> T {
self.read().clone()
}
@@ -291,7 +329,7 @@ impl<T: 'static> PartialEq for Signal<T> {
}
impl<T> Deref for Signal<T> {
type Target = dyn Fn() -> Ref<'static, T>;
type Target = dyn Fn() -> GenerationalRef<T>;
fn deref(&self) -> &Self::Target {
// https://github.com/dtolnay/case-studies/tree/master/callable-types
@@ -334,17 +372,17 @@ impl<T: 'static> Drop for SignalSubscriberDrop<T> {
}
/// A mutable reference to a signal's value.
pub struct Write<'a, T: 'static, I: 'static = T> {
write: RefMut<'a, T>,
pub struct Write<T: 'static, I: 'static = T> {
write: GenerationalRefMut<T>,
signal: SignalSubscriberDrop<I>,
}
impl<'a, T: 'static, I: 'static> Write<'a, T, I> {
impl<T: 'static, I: 'static> Write<T, I> {
/// Map the mutable reference to the signal's value to a new type.
pub fn map<O>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<'a, O, I> {
pub fn map<O>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<O, I> {
let Self { write, signal } = myself;
Write {
write: RefMut::map(write, f),
write: GenerationalRefMut::map(write, f),
signal,
}
}
@@ -353,14 +391,14 @@ impl<'a, T: 'static, I: 'static> Write<'a, T, I> {
pub fn filter_map<O>(
myself: Self,
f: impl FnOnce(&mut T) -> Option<&mut O>,
) -> Option<Write<'a, O, I>> {
) -> Option<Write<O, I>> {
let Self { write, signal } = myself;
let write = RefMut::filter_map(write, f).ok();
let write = GenerationalRefMut::filter_map(write, f);
write.map(|write| Write { write, signal })
}
}
impl<'a, T: 'static, I: 'static> Deref for Write<'a, T, I> {
impl<T: 'static, I: 'static> Deref for Write<T, I> {
type Target = T;
fn deref(&self) -> &Self::Target {
@@ -368,7 +406,7 @@ impl<'a, T: 'static, I: 'static> Deref for Write<'a, T, I> {
}
}
impl<T, I> DerefMut for Write<'_, T, I> {
impl<T, I> DerefMut for Write<T, I> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.write
}
@@ -393,7 +431,8 @@ impl<T: 'static> ReadOnlySignal<T> {
/// Get the current value of the signal. This will subscribe the current scope to the signal. If you would like to read the signal without subscribing to it, you can use [`Self::peak`] instead.
///
/// If the signal has been dropped, this will panic.
pub fn read(&self) -> Ref<T> {
#[track_caller]
pub fn read(&self) -> GenerationalRef<T> {
self.inner.read()
}
@@ -405,6 +444,7 @@ impl<T: 'static> ReadOnlySignal<T> {
}
/// Run a closure with a reference to the signal's value.
#[track_caller]
pub fn with<O>(&self, f: impl FnOnce(&T) -> O) -> O {
self.inner.with(f)
}
@@ -424,7 +464,7 @@ impl<T: 'static> PartialEq for ReadOnlySignal<T> {
}
impl<T> Deref for ReadOnlySignal<T> {
type Target = dyn Fn() -> Ref<'static, T>;
type Target = dyn Fn() -> GenerationalRef<T>;
fn deref(&self) -> &Self::Target {
// https://github.com/dtolnay/case-studies/tree/master/callable-types