Переглянути джерело

Restore set compare hook (#2287)

* restore set compare hook

* fix use_set_compare doc example

* add a hook to compare a value with a set

* implement partialeq for SetCompare

* export the new hook

* fix the use_set_compare hook
Evan Almloff 1 рік тому
батько
коміт
1d72ef16c4

+ 3 - 0
packages/hooks/src/lib.rs

@@ -92,3 +92,6 @@ pub use use_hook_did_run::*;
 
 mod use_signal;
 pub use use_signal::*;
+
+mod use_set_compare;
+pub use use_set_compare::*;

+ 1 - 2
packages/hooks/src/use_future.rs

@@ -1,8 +1,7 @@
 #![allow(missing_docs)]
 use crate::{use_callback, use_hook_did_run, use_signal, UseCallback};
-use dioxus_core::{prelude::*, Task};
+use dioxus_core::prelude::*;
 use dioxus_signals::*;
-use dioxus_signals::{Readable, Writable};
 use std::future::Future;
 use std::ops::Deref;
 

+ 53 - 0
packages/hooks/src/use_set_compare.rs

@@ -0,0 +1,53 @@
+use std::hash::Hash;
+
+use dioxus_core::prelude::*;
+use dioxus_signals::{ReadOnlySignal, SetCompare};
+
+/// Creates a new SetCompare which efficiently tracks when a value changes to check if it is equal to a set of values.
+///
+/// Generally, you shouldn't need to use this hook. Instead you can use [`crate::use_memo`]. If you have many values that you need to compare to a single value, this hook will change updates from O(n) to O(1) where n is the number of values you are comparing to.
+///
+/// ```rust
+/// use dioxus::prelude::*;
+///
+/// fn App() -> Element {
+///     let mut count = use_signal(|| 0);
+///     let compare = use_set_compare(move || count());
+///
+///     rsx! {
+///         for i in 0..10 {
+///             // Child will only re-render when i == count
+///             Child { compare, i }
+///         }
+///         button {
+///             // This will only rerender the child with the old and new value of i == count
+///             // Because we are using a set compare, this will be O(1) instead of the O(n) performance of a selector
+///             onclick: move |_| count += 1,
+///             "Increment"
+///         }
+///     }
+/// }
+///
+/// #[component]
+/// fn Child(i: usize, compare: SetCompare<usize>) -> Element {
+///     let active = use_set_compare_equal(i, compare);
+///     if active() {
+///         rsx! { "Active" }
+///     } else {
+///         rsx! { "Inactive" }
+///     }
+/// }
+/// ```
+#[must_use]
+pub fn use_set_compare<R: Eq + Hash>(f: impl FnMut() -> R + 'static) -> SetCompare<R> {
+    use_hook(move || SetCompare::new(f))
+}
+
+/// A hook that returns true if the value is equal to the value in the set compare.
+#[must_use]
+pub fn use_set_compare_equal<R: Eq + Hash>(
+    value: R,
+    mut compare: SetCompare<R>,
+) -> ReadOnlySignal<bool> {
+    use_hook(move || compare.equal(value))
+}

+ 0 - 143
packages/signals/src/comparer.rs

@@ -1,143 +0,0 @@
-use crate::write::Writable;
-use std::hash::Hash;
-
-use crate::read::Readable;
-use dioxus_core::prelude::*;
-use generational_box::{Storage, UnsyncStorage};
-
-use crate::{CopyValue, Effect, ReadOnlySignal, Signal, SignalData};
-use rustc_hash::FxHashMap;
-
-/// An object that can efficiently compare a value to a set of values.
-#[derive(Debug)]
-pub struct Comparer<R: 'static, S: Storage<SignalData<bool>> = UnsyncStorage> {
-    subscribers: CopyValue<FxHashMap<R, Signal<bool, S>>>,
-}
-
-impl<R: Eq + Hash> Comparer<R> {
-    /// Creates a new Comparer which efficiently tracks when a value changes to check if it is equal to a set of values.
-    ///
-    /// Generally, you shouldn't need to use this hook. Instead you can use [`crate::use_memo`]. If you have many values that you need to compare to a single value, this hook will change updates from O(n) to O(1) where n is the number of values you are comparing to.
-    pub fn new(mut f: impl FnMut() -> R + 'static) -> Comparer<R> {
-        let subscribers: CopyValue<FxHashMap<R, Signal<bool>>> =
-            CopyValue::new(FxHashMap::default());
-        let mut previous = CopyValue::new(None);
-
-        Effect::new(move || {
-            let subscribers = subscribers.read();
-            let mut previous = previous.write();
-
-            if let Some(previous) = previous.take() {
-                if let Some(mut value) = subscribers.get(&previous).cloned() {
-                    value.set(false)
-                }
-            }
-
-            let current = f();
-
-            if let Some(mut value) = subscribers.get(&current).cloned() {
-                *value.write() = true;
-            }
-
-            *previous = Some(current);
-        });
-
-        Comparer { subscribers }
-    }
-}
-
-impl<R: Eq + Hash, S: Storage<SignalData<bool>>> Comparer<R, S> {
-    /// Creates a new Comparer that may be `Sync + Send` which efficiently tracks when a value changes to check if it is equal to a set of values.
-    ///
-    /// Generally, you shouldn't need to use this hook. Instead you can use [`crate::use_memo`]. If you have many values that you need to compare to a single value, this hook will change updates from O(n) to O(1) where n is the number of values you are comparing to.
-    pub fn new_maybe_sync(mut f: impl FnMut() -> R + 'static) -> Comparer<R> {
-        let subscribers: CopyValue<FxHashMap<R, Signal<bool>>> =
-            CopyValue::new(FxHashMap::default());
-        let mut previous = CopyValue::new(None);
-
-        Effect::new(move || {
-            let subscribers = subscribers.read();
-            let mut previous = previous.write();
-
-            if let Some(previous) = previous.take() {
-                if let Some(mut value) = subscribers.get(&previous).cloned() {
-                    *value.write() = false;
-                }
-            }
-
-            let current = f();
-
-            if let Some(mut value) = subscribers.get(&current).cloned() {
-                *value.write() = true;
-            }
-
-            *previous = Some(current);
-        });
-
-        Comparer { subscribers }
-    }
-
-    /// Returns a signal which is true when the value is equal to the value passed to this function.
-    pub fn equal(&mut self, value: R) -> ReadOnlySignal<bool, S> {
-        let subscribers = self.subscribers.write();
-
-        match subscribers.get(&value) {
-            Some(&signal) => signal.into(),
-            None => {
-                drop(subscribers);
-                let mut subscribers = self.subscribers.write();
-                let signal = Signal::new_maybe_sync(false);
-                subscribers.insert(value, signal);
-                signal.into()
-            }
-        }
-    }
-}
-
-impl<R, S: Storage<SignalData<bool>>> Clone for Comparer<R, S> {
-    fn clone(&self) -> Self {
-        *self
-    }
-}
-
-impl<R, S: Storage<SignalData<bool>>> Copy for Comparer<R, S> {}
-
-/// Creates a new Comparer which efficiently tracks when a value changes to check if it is equal to a set of values.
-///
-/// Generally, you shouldn't need to use this hook. Instead you can use [`crate::use_memo`]. If you have many values that you need to compare to a single value, this hook will change updates from O(n) to O(1) where n is the number of values you are comparing to.
-///
-/// ```rust
-/// use dioxus::prelude::*;
-/// use dioxus_signals::*;
-///
-/// fn App() -> Element {
-///     let mut count = use_signal(cx, || 0);
-///     let comparer = use_comparer(cx, move || count.value());
-///
-///     render! {
-///         for i in 0..10 {
-///             // Child will only re-render when i == count
-///             Child { active: comparer.equal(i) }
-///         }
-///         button {
-///             // This will only rerender the child with the old and new value of i == count
-///             // Because we are using a comparer, this will be O(1) instead of the O(n) performance of a selector
-///             onclick: move |_| count += 1,
-///             "Increment"
-///         }
-///     }
-/// }
-///
-/// #[component]
-/// fn Child(active: ReadOnlySignal<bool>) -> Element {
-///     if *active() {
-///         render! { "Active" }
-///     } else {
-///         render! { "Inactive" }
-///     }
-/// }
-/// ```
-#[must_use]
-pub fn use_comparer<R: Eq + Hash>(f: impl FnMut() -> R + 'static) -> Comparer<R> {
-    use_hook(move || Comparer::new(f))
-}

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

@@ -16,8 +16,8 @@ pub use read_only_signal::*;
 mod map;
 pub use map::*;
 
-// mod comparer;
-// pub use comparer::*;
+mod set_compare;
+pub use set_compare::*;
 
 mod memo;
 pub use memo::*;

+ 99 - 0
packages/signals/src/set_compare.rs

@@ -0,0 +1,99 @@
+use crate::{write::Writable, ReactiveContext};
+use std::hash::Hash;
+
+use crate::read::Readable;
+use dioxus_core::prelude::*;
+use futures_util::StreamExt;
+use generational_box::{Storage, UnsyncStorage};
+
+use crate::{CopyValue, ReadOnlySignal, Signal, SignalData};
+use rustc_hash::FxHashMap;
+
+/// An object that can efficiently compare a value to a set of values.
+#[derive(Debug)]
+pub struct SetCompare<R: 'static, S: Storage<SignalData<bool>> = UnsyncStorage> {
+    subscribers: CopyValue<FxHashMap<R, Signal<bool, S>>>,
+}
+
+impl<R: Eq + Hash> SetCompare<R> {
+    /// Creates a new [`SetCompare`] which efficiently tracks when a value changes to check if it is equal to a set of values.
+    ///
+    /// Generally, you shouldn't need to use this hook. Instead you can use [`crate::use_memo`]. If you have many values that you need to compare to a single value, this hook will change updates from O(n) to O(1) where n is the number of values you are comparing to.
+    #[track_caller]
+    pub fn new(f: impl FnMut() -> R + 'static) -> SetCompare<R> {
+        Self::new_maybe_sync(f)
+    }
+}
+
+impl<R: Eq + Hash, S: Storage<SignalData<bool>>> SetCompare<R, S> {
+    /// Creates a new [`SetCompare`] that may be `Sync + Send` which efficiently tracks when a value changes to check if it is equal to a set of values.
+    ///
+    /// Generally, you shouldn't need to use this hook. Instead you can use [`crate::use_memo`]. If you have many values that you need to compare to a single value, this hook will change updates from O(n) to O(1) where n is the number of values you are comparing to.
+    #[track_caller]
+    pub fn new_maybe_sync(mut f: impl FnMut() -> R + 'static) -> SetCompare<R> {
+        let subscribers: CopyValue<FxHashMap<R, Signal<bool>>> =
+            CopyValue::new(FxHashMap::default());
+        let mut previous = CopyValue::new(None);
+
+        let mut recompute = move || {
+            let subscribers = subscribers.read();
+            let mut previous = previous.write();
+
+            if let Some(previous) = previous.take() {
+                if let Some(mut value) = subscribers.get(&previous).cloned() {
+                    *value.write() = false;
+                }
+            }
+
+            let current = f();
+
+            if let Some(mut value) = subscribers.get(&current).cloned() {
+                *value.write() = true;
+            }
+
+            *previous = Some(current);
+        };
+        let (rc, mut changed) = ReactiveContext::new();
+        spawn(async move {
+            loop {
+                // Recompute the value
+                rc.run_in(&mut recompute);
+
+                // Wait for context to change
+                let _ = changed.next().await;
+            }
+        });
+
+        SetCompare { subscribers }
+    }
+
+    /// Returns a signal which is true when the value is equal to the value passed to this function.
+    pub fn equal(&mut self, value: R) -> ReadOnlySignal<bool, S> {
+        let subscribers = self.subscribers.write();
+
+        match subscribers.get(&value) {
+            Some(&signal) => signal.into(),
+            None => {
+                drop(subscribers);
+                let mut subscribers = self.subscribers.write();
+                let signal = Signal::new_maybe_sync(false);
+                subscribers.insert(value, signal);
+                signal.into()
+            }
+        }
+    }
+}
+
+impl<R: 'static, S: Storage<SignalData<bool>>> PartialEq for SetCompare<R, S> {
+    fn eq(&self, other: &Self) -> bool {
+        self.subscribers == other.subscribers
+    }
+}
+
+impl<R, S: Storage<SignalData<bool>>> Clone for SetCompare<R, S> {
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+
+impl<R, S: Storage<SignalData<bool>>> Copy for SetCompare<R, S> {}