Explorar el Código

Merge pull request #297 from DioxusLabs/jk/use-ref-memo

feat: memoize useref by tracking mutations
Jonathan Kelley hace 3 años
padre
commit
9846e0947b
Se han modificado 1 ficheros con 39 adiciones y 13 borrados
  1. 39 13
      packages/hooks/src/useref.rs

+ 39 - 13
packages/hooks/src/useref.rs

@@ -1,6 +1,6 @@
 use dioxus_core::ScopeState;
 use std::{
-    cell::{Ref, RefCell, RefMut},
+    cell::{Cell, Ref, RefCell, RefMut},
     rc::Rc,
     sync::Arc,
 };
@@ -114,16 +114,27 @@ pub fn use_ref<'a, T: 'static>(
     cx: &'a ScopeState,
     initialize_refcell: impl FnOnce() -> T,
 ) -> &'a UseRef<T> {
-    cx.use_hook(|_| UseRef {
+    let hook = cx.use_hook(|_| UseRef {
         update: cx.schedule_update(),
         value: Rc::new(RefCell::new(initialize_refcell())),
-    })
+        dirty: Rc::new(Cell::new(false)),
+        gen: 0,
+    });
+
+    if hook.dirty.get() {
+        hook.gen += 1;
+        hook.dirty.set(false);
+    }
+
+    hook
 }
 
 /// A type created by the [`use_ref`] hook. See its documentation for more details.
 pub struct UseRef<T> {
     update: Arc<dyn Fn()>,
     value: Rc<RefCell<T>>,
+    dirty: Rc<Cell<bool>>,
+    gen: usize,
 }
 
 impl<T> Clone for UseRef<T> {
@@ -131,6 +142,8 @@ impl<T> Clone for UseRef<T> {
         Self {
             update: self.update.clone(),
             value: self.value.clone(),
+            dirty: self.dirty.clone(),
+            gen: self.gen,
         }
     }
 }
@@ -146,16 +159,6 @@ impl<T> UseRef<T> {
         self.value.borrow()
     }
 
-    /// Set the curernt value to `new_value`. This will mark the component as "dirty"
-    ///
-    /// This change will propogate immediately, so any other contexts that are
-    /// using this RefCell will also be affected. If called during an async context,
-    /// the component will not be re-rendered until the next `.await` call.
-    pub fn set(&self, new: T) {
-        *self.value.borrow_mut() = new;
-        self.needs_update();
-    }
-
     /// Mutably unlock the value in the RefCell. This will mark the component as "dirty"
     ///
     /// Uses to `write` should be as short as possible.
@@ -168,6 +171,16 @@ impl<T> UseRef<T> {
         self.value.borrow_mut()
     }
 
+    /// Set the curernt value to `new_value`. This will mark the component as "dirty"
+    ///
+    /// This change will propogate immediately, so any other contexts that are
+    /// using this RefCell will also be affected. If called during an async context,
+    /// the component will not be re-rendered until the next `.await` call.
+    pub fn set(&self, new: T) {
+        *self.value.borrow_mut() = new;
+        self.needs_update();
+    }
+
     /// Mutably unlock the value in the RefCell. This will not mark the component as dirty.
     /// This is useful if you want to do some work without causing the component to re-render.
     ///
@@ -223,6 +236,19 @@ impl<T> UseRef<T> {
     /// This will cause the component to be re-rendered after the current scope
     /// has ended or the current async task has been yielded through await.
     pub fn needs_update(&self) {
+        self.dirty.set(true);
         (self.update)();
     }
 }
+
+// UseRef memoizes not on value but on cell
+// Memoizes on "generation" - so it will cause a re-render if the value changes
+impl<T> PartialEq for UseRef<T> {
+    fn eq(&self, other: &Self) -> bool {
+        if Rc::ptr_eq(&self.value, &other.value) {
+            self.gen == other.gen
+        } else {
+            false
+        }
+    }
+}