Browse Source

create read only signal

Evan Almloff 1 year ago
parent
commit
646c161c7d

+ 2 - 2
Cargo.toml

@@ -25,7 +25,7 @@ members = [
     "packages/native-core",
     "packages/native-core-macro",
     "packages/rsx-rosetta",
-    "packages/generational_box",
+    "packages/generational-box",
     "packages/signals",
     "packages/hot-reload",
     "packages/fullstack",
@@ -77,7 +77,7 @@ dioxus-native-core = { path = "packages/native-core", version = "0.4.0" }
 dioxus-native-core-macro = { path = "packages/native-core-macro", version = "0.4.0" }
 rsx-rosetta = { path = "packages/rsx-rosetta", version = "0.4.0" }
 dioxus-signals = { path = "packages/signals" }
-generational-box = { path = "packages/generational_box" }
+generational-box = { path = "packages/generational-box" }
 dioxus-hot-reload = { path = "packages/hot-reload", version = "0.4.0" }
 dioxus-fullstack = { path = "packages/fullstack", version = "0.4.1"  }
 dioxus_server_macro = { path = "packages/server-macro", version = "0.4.1" }

+ 0 - 0
packages/generational_box/Cargo.toml → packages/generational-box/Cargo.toml


+ 0 - 0
packages/generational_box/src/lib.rs → packages/generational-box/src/lib.rs


+ 31 - 20
packages/signals/src/impls.rs

@@ -1,5 +1,5 @@
 use crate::rt::CopyValue;
-use crate::{Signal, Write};
+use crate::signal::{ReadOnlySignal, Signal, Write};
 
 use std::cell::{Ref, RefMut};
 
@@ -8,11 +8,11 @@ use std::{
     ops::{Add, Div, Mul, Sub},
 };
 
-macro_rules! impls {
+macro_rules! read_impls {
     ($ty:ident) => {
         impl<T: Default + 'static> Default for $ty<T> {
             fn default() -> Self {
-                Self::new(T::default())
+                Self::new(Default::default())
             }
         }
 
@@ -36,6 +36,29 @@ macro_rules! impls {
             }
         }
 
+        impl<T: 'static> $ty<Vec<T>> {
+            pub fn get(&self, index: usize) -> Option<Ref<'_, T>> {
+                Ref::filter_map(self.read(), |v| v.get(index)).ok()
+            }
+        }
+
+        impl<T: 'static> $ty<Option<T>> {
+            pub fn unwrap(&self) -> T
+            where
+                T: Clone,
+            {
+                self.with(|v| v.clone()).unwrap()
+            }
+
+            pub fn as_ref(&self) -> Option<Ref<'_, T>> {
+                Ref::filter_map(self.read(), |v| v.as_ref()).ok()
+            }
+        }
+    };
+}
+
+macro_rules! write_impls {
+    ($ty:ident) => {
         impl<T: Add<Output = T> + Copy + 'static> std::ops::AddAssign<T> for $ty<T> {
             fn add_assign(&mut self, rhs: T) {
                 self.with_mut(|v| *v = *v + rhs)
@@ -100,10 +123,6 @@ macro_rules! impls {
             pub fn split_off(&self, at: usize) -> Vec<T> {
                 self.with_mut(|v| v.split_off(at))
             }
-
-            pub fn get(&self, index: usize) -> Option<Ref<'_, T>> {
-                Ref::filter_map(self.read(), |v| v.get(index)).ok()
-            }
         }
 
         impl<T: 'static> $ty<Option<T>> {
@@ -115,17 +134,6 @@ macro_rules! impls {
                 self.with_mut(|v| v.replace(value))
             }
 
-            pub fn unwrap(&self) -> T
-            where
-                T: Clone,
-            {
-                self.with(|v| v.clone()).unwrap()
-            }
-
-            pub fn as_ref(&self) -> Option<Ref<'_, T>> {
-                Ref::filter_map(self.read(), |v| v.as_ref()).ok()
-            }
-
             pub fn get_or_insert(&self, default: T) -> Ref<'_, T> {
                 self.get_or_insert_with(|| default)
             }
@@ -144,8 +152,11 @@ macro_rules! impls {
     };
 }
 
-impls!(CopyValue);
-impls!(Signal);
+read_impls!(CopyValue);
+write_impls!(CopyValue);
+read_impls!(Signal);
+write_impls!(Signal);
+read_impls!(ReadOnlySignal);
 
 pub struct CopyValueIterator<T: 'static> {
     index: usize,

+ 2 - 209
packages/signals/src/lib.rs

@@ -1,10 +1,3 @@
-use std::{
-    cell::{Ref, RefCell, RefMut},
-    ops::{Deref, DerefMut},
-    rc::Rc,
-    sync::Arc,
-};
-
 mod rt;
 pub use rt::*;
 mod effect;
@@ -12,205 +5,5 @@ pub use effect::*;
 mod impls;
 mod memo;
 pub use memo::*;
-
-use dioxus_core::{
-    prelude::{current_scope_id, has_context, provide_context, schedule_update_any},
-    ScopeId, ScopeState,
-};
-
-pub fn use_signal<T: 'static>(cx: &ScopeState, f: impl FnOnce() -> T) -> Signal<T> {
-    *cx.use_hook(|| Signal::new(f()))
-}
-
-#[derive(Clone)]
-struct Unsubscriber {
-    scope: ScopeId,
-    subscribers: Rc<RefCell<Vec<Rc<RefCell<Vec<ScopeId>>>>>>,
-}
-
-impl Drop for Unsubscriber {
-    fn drop(&mut self) {
-        for subscribers in self.subscribers.borrow().iter() {
-            subscribers.borrow_mut().retain(|s| *s != self.scope);
-        }
-    }
-}
-
-fn current_unsubscriber() -> Unsubscriber {
-    match has_context() {
-        Some(rt) => rt,
-        None => {
-            let owner = Unsubscriber {
-                scope: current_scope_id().expect("in a virtual dom"),
-                subscribers: Default::default(),
-            };
-            provide_context(owner).expect("in a virtual dom")
-        }
-    }
-}
-
-struct SignalData<T> {
-    subscribers: Rc<RefCell<Vec<ScopeId>>>,
-    effect_subscribers: Rc<RefCell<Vec<Effect>>>,
-    update_any: Arc<dyn Fn(ScopeId)>,
-    pub(crate) value: T,
-}
-
-pub struct Signal<T: 'static> {
-    pub(crate) inner: CopyValue<SignalData<T>>,
-}
-
-impl<T: 'static> Signal<T> {
-    pub fn new(value: T) -> Self {
-        Self {
-            inner: CopyValue::new(SignalData {
-                subscribers: Default::default(),
-                effect_subscribers: Default::default(),
-                update_any: schedule_update_any().expect("in a virtual dom"),
-                value,
-            }),
-        }
-    }
-
-    pub fn origin_scope(&self) -> ScopeId {
-        self.inner.origin_scope()
-    }
-
-    pub fn read(&self) -> Ref<T> {
-        let inner = self.inner.read();
-        if let Some(effect) = Effect::current() {
-            let mut effect_subscribers = inner.effect_subscribers.borrow_mut();
-            if !effect_subscribers.contains(&effect) {
-                effect_subscribers.push(effect);
-            }
-        } else if let Some(current_scope_id) = current_scope_id() {
-            log::trace!(
-                "{:?} subscribed to {:?}",
-                self.inner.value,
-                current_scope_id
-            );
-            let mut subscribers = inner.subscribers.borrow_mut();
-            if !subscribers.contains(&current_scope_id) {
-                subscribers.push(current_scope_id);
-                drop(subscribers);
-                let unsubscriber = current_unsubscriber();
-                inner.subscribers.borrow_mut().push(unsubscriber.scope);
-            }
-        }
-        Ref::map(inner, |v| &v.value)
-    }
-
-    pub fn write(&self) -> Write<'_, T> {
-        let inner = self.inner.write();
-        let borrow = RefMut::map(inner, |v| &mut v.value);
-        Write {
-            write: borrow,
-            signal: SignalSubscriberDrop { signal: *self },
-        }
-    }
-
-    fn update_subscribers(&self) {
-        {
-            let inner = self.inner.read();
-            for &scope_id in &*inner.subscribers.borrow() {
-                log::trace!(
-                    "Write on {:?} triggered update on {:?}",
-                    self.inner.value,
-                    scope_id
-                );
-                (inner.update_any)(scope_id);
-            }
-        }
-
-        let subscribers = {
-            let self_read = self.inner.read();
-            let mut effects = self_read.effect_subscribers.borrow_mut();
-            std::mem::take(&mut *effects)
-        };
-        for effect in subscribers {
-            log::trace!(
-                "Write on {:?} triggered effect {:?}",
-                self.inner.value,
-                effect
-            );
-            effect.try_run();
-        }
-    }
-
-    pub fn set(&self, value: T) {
-        *self.write() = value;
-    }
-
-    pub fn with<O>(&self, f: impl FnOnce(&T) -> O) -> O {
-        let write = self.read();
-        f(&*write)
-    }
-
-    pub fn with_mut<O>(&self, f: impl FnOnce(&mut T) -> O) -> O {
-        let mut write = self.write();
-        f(&mut *write)
-    }
-}
-
-impl<T: Clone + 'static> Signal<T> {
-    pub fn value(&self) -> T {
-        self.read().clone()
-    }
-}
-
-impl<T: 'static> PartialEq for Signal<T> {
-    fn eq(&self, other: &Self) -> bool {
-        self.inner == other.inner
-    }
-}
-
-struct SignalSubscriberDrop<T: 'static> {
-    signal: Signal<T>,
-}
-
-impl<T: 'static> Drop for SignalSubscriberDrop<T> {
-    fn drop(&mut self) {
-        self.signal.update_subscribers();
-    }
-}
-
-pub struct Write<'a, T: 'static, I: 'static = T> {
-    write: RefMut<'a, T>,
-    signal: SignalSubscriberDrop<I>,
-}
-
-impl<'a, T: 'static, I: 'static> Write<'a, T, I> {
-    pub fn map<O>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<'a, O, I> {
-        let Self { write, signal } = myself;
-        Write {
-            write: RefMut::map(write, f),
-            signal,
-        }
-    }
-
-    pub fn filter_map<O>(
-        myself: Self,
-        f: impl FnOnce(&mut T) -> Option<&mut O>,
-    ) -> Option<Write<'a, O, I>> {
-        let Self { write, signal } = myself;
-        let write = RefMut::filter_map(write, f).ok();
-        write.map(|write| Write {
-            write,
-            signal: signal,
-        })
-    }
-}
-
-impl<'a, T: 'static> Deref for Write<'a, T> {
-    type Target = T;
-
-    fn deref(&self) -> &Self::Target {
-        &*self.write
-    }
-}
-
-impl<T> DerefMut for Write<'_, T> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut *self.write
-    }
-}
+pub(crate) mod signal;
+pub use signal::*;

+ 8 - 5
packages/signals/src/memo.rs

@@ -1,12 +1,15 @@
 use dioxus_core::prelude::*;
 
-use crate::{get_effect_stack, CopyValue, Effect, Signal, SignalData};
+use crate::{get_effect_stack, signal::SignalData, CopyValue, Effect, ReadOnlySignal, Signal};
 
-pub fn use_memo<R: PartialEq>(cx: &ScopeState, f: impl FnMut() -> R + 'static) -> Signal<R> {
-    *cx.use_hook(|| memo(f))
+pub fn use_selector<R: PartialEq>(
+    cx: &ScopeState,
+    f: impl FnMut() -> R + 'static,
+) -> ReadOnlySignal<R> {
+    *cx.use_hook(|| selector(f))
 }
 
-pub fn memo<R: PartialEq>(mut f: impl FnMut() -> R + 'static) -> Signal<R> {
+fn selector<R: PartialEq>(mut f: impl FnMut() -> R + 'static) -> ReadOnlySignal<R> {
     let state = Signal::<R> {
         inner: CopyValue::invalid(),
     };
@@ -38,5 +41,5 @@ pub fn memo<R: PartialEq>(mut f: impl FnMut() -> R + 'static) -> Signal<R> {
         }
     }));
 
-    state
+    ReadOnlySignal::new(state)
 }

+ 244 - 0
packages/signals/src/signal.rs

@@ -0,0 +1,244 @@
+use std::{
+    cell::{Ref, RefCell, RefMut},
+    ops::{Deref, DerefMut},
+    rc::Rc,
+    sync::Arc,
+};
+
+use dioxus_core::{
+    prelude::{current_scope_id, has_context, provide_context, schedule_update_any},
+    ScopeId, ScopeState,
+};
+
+use crate::{CopyValue, Effect};
+
+pub fn use_signal<T: 'static>(cx: &ScopeState, f: impl FnOnce() -> T) -> Signal<T> {
+    *cx.use_hook(|| Signal::new(f()))
+}
+
+#[derive(Clone)]
+struct Unsubscriber {
+    scope: ScopeId,
+    subscribers: Rc<RefCell<Vec<Rc<RefCell<Vec<ScopeId>>>>>>,
+}
+
+impl Drop for Unsubscriber {
+    fn drop(&mut self) {
+        for subscribers in self.subscribers.borrow().iter() {
+            subscribers.borrow_mut().retain(|s| *s != self.scope);
+        }
+    }
+}
+
+fn current_unsubscriber() -> Unsubscriber {
+    match has_context() {
+        Some(rt) => rt,
+        None => {
+            let owner = Unsubscriber {
+                scope: current_scope_id().expect("in a virtual dom"),
+                subscribers: Default::default(),
+            };
+            provide_context(owner).expect("in a virtual dom")
+        }
+    }
+}
+
+pub(crate) struct SignalData<T> {
+    pub(crate) subscribers: Rc<RefCell<Vec<ScopeId>>>,
+    pub(crate) effect_subscribers: Rc<RefCell<Vec<Effect>>>,
+    pub(crate) update_any: Arc<dyn Fn(ScopeId)>,
+    pub(crate) value: T,
+}
+
+pub struct Signal<T: 'static> {
+    pub(crate) inner: CopyValue<SignalData<T>>,
+}
+
+impl<T: 'static> Signal<T> {
+    pub fn new(value: T) -> Self {
+        Self {
+            inner: CopyValue::new(SignalData {
+                subscribers: Default::default(),
+                effect_subscribers: Default::default(),
+                update_any: schedule_update_any().expect("in a virtual dom"),
+                value,
+            }),
+        }
+    }
+
+    pub fn origin_scope(&self) -> ScopeId {
+        self.inner.origin_scope()
+    }
+
+    pub fn read(&self) -> Ref<T> {
+        let inner = self.inner.read();
+        if let Some(effect) = Effect::current() {
+            let mut effect_subscribers = inner.effect_subscribers.borrow_mut();
+            if !effect_subscribers.contains(&effect) {
+                effect_subscribers.push(effect);
+            }
+        } else if let Some(current_scope_id) = current_scope_id() {
+            log::trace!(
+                "{:?} subscribed to {:?}",
+                self.inner.value,
+                current_scope_id
+            );
+            let mut subscribers = inner.subscribers.borrow_mut();
+            if !subscribers.contains(&current_scope_id) {
+                subscribers.push(current_scope_id);
+                drop(subscribers);
+                let unsubscriber = current_unsubscriber();
+                inner.subscribers.borrow_mut().push(unsubscriber.scope);
+            }
+        }
+        Ref::map(inner, |v| &v.value)
+    }
+
+    pub fn write(&self) -> Write<'_, T> {
+        let inner = self.inner.write();
+        let borrow = RefMut::map(inner, |v| &mut v.value);
+        Write {
+            write: borrow,
+            signal: SignalSubscriberDrop { signal: *self },
+        }
+    }
+
+    fn update_subscribers(&self) {
+        {
+            let inner = self.inner.read();
+            for &scope_id in &*inner.subscribers.borrow() {
+                log::trace!(
+                    "Write on {:?} triggered update on {:?}",
+                    self.inner.value,
+                    scope_id
+                );
+                (inner.update_any)(scope_id);
+            }
+        }
+
+        let subscribers = {
+            let self_read = self.inner.read();
+            let mut effects = self_read.effect_subscribers.borrow_mut();
+            std::mem::take(&mut *effects)
+        };
+        for effect in subscribers {
+            log::trace!(
+                "Write on {:?} triggered effect {:?}",
+                self.inner.value,
+                effect
+            );
+            effect.try_run();
+        }
+    }
+
+    pub fn set(&self, value: T) {
+        *self.write() = value;
+    }
+
+    pub fn with<O>(&self, f: impl FnOnce(&T) -> O) -> O {
+        let write = self.read();
+        f(&*write)
+    }
+
+    pub fn with_mut<O>(&self, f: impl FnOnce(&mut T) -> O) -> O {
+        let mut write = self.write();
+        f(&mut *write)
+    }
+}
+
+impl<T: Clone + 'static> Signal<T> {
+    pub fn value(&self) -> T {
+        self.read().clone()
+    }
+}
+
+impl<T: 'static> PartialEq for Signal<T> {
+    fn eq(&self, other: &Self) -> bool {
+        self.inner == other.inner
+    }
+}
+
+struct SignalSubscriberDrop<T: 'static> {
+    signal: Signal<T>,
+}
+
+impl<T: 'static> Drop for SignalSubscriberDrop<T> {
+    fn drop(&mut self) {
+        self.signal.update_subscribers();
+    }
+}
+
+pub struct Write<'a, T: 'static, I: 'static = T> {
+    write: RefMut<'a, T>,
+    signal: SignalSubscriberDrop<I>,
+}
+
+impl<'a, T: 'static, I: 'static> Write<'a, T, I> {
+    pub fn map<O>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<'a, O, I> {
+        let Self { write, signal } = myself;
+        Write {
+            write: RefMut::map(write, f),
+            signal,
+        }
+    }
+
+    pub fn filter_map<O>(
+        myself: Self,
+        f: impl FnOnce(&mut T) -> Option<&mut O>,
+    ) -> Option<Write<'a, O, I>> {
+        let Self { write, signal } = myself;
+        let write = RefMut::filter_map(write, f).ok();
+        write.map(|write| Write {
+            write,
+            signal: signal,
+        })
+    }
+}
+
+impl<'a, T: 'static> Deref for Write<'a, T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &*self.write
+    }
+}
+
+impl<T> DerefMut for Write<'_, T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut *self.write
+    }
+}
+
+pub struct ReadOnlySignal<T: 'static> {
+    inner: Signal<T>,
+}
+
+impl<T: 'static> ReadOnlySignal<T> {
+    pub fn new(signal: Signal<T>) -> Self {
+        Self { inner: signal }
+    }
+
+    pub fn origin_scope(&self) -> ScopeId {
+        self.inner.origin_scope()
+    }
+
+    pub fn read(&self) -> Ref<T> {
+        self.inner.read()
+    }
+
+    pub fn with<O>(&self, f: impl FnOnce(&T) -> O) -> O {
+        self.inner.with(f)
+    }
+}
+
+impl<T: Clone + 'static> ReadOnlySignal<T> {
+    pub fn value(&self) -> T {
+        self.read().clone()
+    }
+}
+
+impl<T: 'static> PartialEq for ReadOnlySignal<T> {
+    fn eq(&self, other: &Self) -> bool {
+        self.inner == other.inner
+    }
+}