Evan Almloff 1 år sedan
förälder
incheckning
7f2049b647

+ 17 - 0
packages/copy/src/lib.rs

@@ -209,6 +209,12 @@ impl<T: 'static> CopyHandle<T> {
         self.try_write().unwrap()
         self.try_write().unwrap()
     }
     }
 
 
+    pub fn set(&self, value: T) {
+        self.validate().then(|| {
+            *self.raw.data.borrow_mut() = Some(Box::new(value));
+        });
+    }
+
     pub fn ptr_eq(&self, other: &Self) -> bool {
     pub fn ptr_eq(&self, other: &Self) -> bool {
         #[cfg(any(debug_assertions, feature = "check_generation"))]
         #[cfg(any(debug_assertions, feature = "check_generation"))]
         {
         {
@@ -316,6 +322,17 @@ impl Owner {
         self.owned.borrow_mut().push(location);
         self.owned.borrow_mut().push(location);
         key
         key
     }
     }
+
+    /// Creates an invalid handle. This is useful for creating a handle that will be filled in later. If you use this before the value is filled in, you will get may get a panic or an out of date value.
+    pub fn invalid<T: 'static>(&self) -> CopyHandle<T> {
+        let location = self.store.claim();
+        CopyHandle {
+            raw: location,
+            #[cfg(any(debug_assertions, feature = "check_generation"))]
+            generation: location.generation.get(),
+            _marker: PhantomData,
+        }
+    }
 }
 }
 
 
 impl Drop for Owner {
 impl Drop for Owner {

+ 10 - 1
packages/signals/src/effect.rs

@@ -1,3 +1,6 @@
+use core::{self, fmt::Debug};
+use std::fmt::{self, Formatter};
+
 use dioxus_core::prelude::*;
 use dioxus_core::prelude::*;
 
 
 use crate::CopyValue;
 use crate::CopyValue;
@@ -19,7 +22,13 @@ pub(crate) fn get_effect_stack() -> EffectStack {
 
 
 #[derive(Copy, Clone, PartialEq)]
 #[derive(Copy, Clone, PartialEq)]
 pub struct Effect {
 pub struct Effect {
-    callback: CopyValue<Box<dyn FnMut()>>,
+    pub(crate) callback: CopyValue<Box<dyn FnMut()>>,
+}
+
+impl Debug for Effect {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        f.write_fmt(format_args!("{:?}", self.callback.value))
+    }
 }
 }
 
 
 impl Effect {
 impl Effect {

+ 9 - 3
packages/signals/src/lib.rs

@@ -8,8 +8,9 @@ mod rt;
 pub use rt::*;
 pub use rt::*;
 mod effect;
 mod effect;
 pub use effect::*;
 pub use effect::*;
-#[macro_use]
 mod impls;
 mod impls;
+mod memo;
+pub use memo::*;
 
 
 use dioxus_core::{
 use dioxus_core::{
     prelude::{current_scope_id, has_context, provide_context, schedule_update_any},
     prelude::{current_scope_id, has_context, provide_context, schedule_update_any},
@@ -55,7 +56,7 @@ struct SignalData<T> {
 }
 }
 
 
 pub struct Signal<T: 'static> {
 pub struct Signal<T: 'static> {
-    inner: CopyValue<SignalData<T>>,
+    pub(crate) inner: CopyValue<SignalData<T>>,
 }
 }
 
 
 impl<T: 'static> Signal<T> {
 impl<T: 'static> Signal<T> {
@@ -115,6 +116,11 @@ impl<T: 'static> Signal<T> {
         let subscribers =
         let subscribers =
             { std::mem::take(&mut *self.inner.read().effect_subscribers.borrow_mut()) };
             { std::mem::take(&mut *self.inner.read().effect_subscribers.borrow_mut()) };
         for effect in subscribers {
         for effect in subscribers {
+            log::trace!(
+                "Write on {:?} triggered effect {:?}",
+                self.inner.value,
+                effect
+            );
             effect.try_run();
             effect.try_run();
         }
         }
 
 
@@ -122,7 +128,7 @@ impl<T: 'static> Signal<T> {
         RefMut::map(inner, |v| &mut v.value)
         RefMut::map(inner, |v| &mut v.value)
     }
     }
 
 
-    pub fn set(&mut self, value: T) {
+    pub fn set(&self, value: T) {
         *self.write() = value;
         *self.write() = value;
     }
     }
 
 

+ 42 - 0
packages/signals/src/memo.rs

@@ -0,0 +1,42 @@
+use dioxus_core::prelude::*;
+
+use crate::{get_effect_stack, CopyValue, Effect, Signal, SignalData};
+
+pub fn use_memo<R: PartialEq>(cx: &ScopeState, f: impl FnMut() -> R + 'static) -> Signal<R> {
+    *cx.use_hook(|| memo(f))
+}
+
+pub fn memo<R: PartialEq>(mut f: impl FnMut() -> R + 'static) -> Signal<R> {
+    let state = Signal::<R> {
+        inner: CopyValue::invalid(),
+    };
+    let effect = Effect {
+        callback: CopyValue::invalid(),
+    };
+
+    {
+        get_effect_stack().effects.write().push(effect);
+    }
+    state.inner.value.set(SignalData {
+        subscribers: Default::default(),
+        effect_subscribers: Default::default(),
+        update_any: schedule_update_any().expect("in a virtual dom"),
+        value: f(),
+    });
+    {
+        get_effect_stack().effects.write().pop();
+    }
+
+    effect.callback.value.set(Box::new(move || {
+        let value = f();
+        let changed = {
+            let state = state.read();
+            value != *state
+        };
+        if changed {
+            state.set(value)
+        }
+    }));
+
+    state
+}

+ 9 - 0
packages/signals/src/rt.rs

@@ -64,6 +64,15 @@ impl<T: 'static> CopyValue<T> {
         }
         }
     }
     }
 
 
+    pub(crate) fn invalid() -> Self {
+        let owner = current_owner();
+
+        Self {
+            value: owner.invalid(),
+            origin_scope: current_scope_id().expect("in a virtual dom"),
+        }
+    }
+
     pub fn origin_scope(&self) -> ScopeId {
     pub fn origin_scope(&self) -> ScopeId {
         self.origin_scope
         self.origin_scope
     }
     }

+ 50 - 0
packages/signals/tests/memo.rs

@@ -0,0 +1,50 @@
+#![allow(unused, non_upper_case_globals, non_snake_case)]
+use std::collections::HashMap;
+use std::rc::Rc;
+
+use dioxus::prelude::*;
+use dioxus_core::ElementId;
+use dioxus_signals::*;
+
+#[test]
+fn memos_rerun() {
+    simple_logger::SimpleLogger::new().init().unwrap();
+
+    #[derive(Default)]
+    struct RunCounter {
+        component: usize,
+        effect: usize,
+    }
+
+    let counter = Rc::new(RefCell::new(RunCounter::default()));
+    let mut dom = VirtualDom::new_with_props(
+        |cx| {
+            let counter = cx.props;
+            counter.borrow_mut().component += 1;
+
+            let mut signal = use_signal(cx, || 0);
+            let memo = cx.use_hook(move || {
+                to_owned![counter];
+                memo(move || {
+                    counter.borrow_mut().effect += 1;
+                    println!("Signal: {:?}", signal);
+                    signal.value()
+                })
+            });
+            assert_eq!(memo.value(), 0);
+            signal += 1;
+            assert_eq!(memo.value(), 1);
+
+            render! {
+                div {}
+            }
+        },
+        counter.clone(),
+    );
+
+    let _ = dom.rebuild().santize();
+
+    let current_counter = counter.borrow();
+    assert_eq!(current_counter.component, 1);
+    assert_eq!(current_counter.effect, 2);
+}