mirror of
https://github.com/DioxusLabs/dioxus
synced 2025-02-16 21:58:25 +00:00
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
This commit is contained in:
parent
fc2b441ee1
commit
1d72ef16c4
6 changed files with 158 additions and 147 deletions
|
@ -92,3 +92,6 @@ pub use use_hook_did_run::*;
|
||||||
|
|
||||||
mod use_signal;
|
mod use_signal;
|
||||||
pub use use_signal::*;
|
pub use use_signal::*;
|
||||||
|
|
||||||
|
mod use_set_compare;
|
||||||
|
pub use use_set_compare::*;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
use crate::{use_callback, use_hook_did_run, use_signal, UseCallback};
|
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::*;
|
||||||
use dioxus_signals::{Readable, Writable};
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
|
53
packages/hooks/src/use_set_compare.rs
Normal file
53
packages/hooks/src/use_set_compare.rs
Normal file
|
@ -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))
|
||||||
|
}
|
|
@ -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(¤t).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(¤t).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))
|
|
||||||
}
|
|
|
@ -16,8 +16,8 @@ pub use read_only_signal::*;
|
||||||
mod map;
|
mod map;
|
||||||
pub use map::*;
|
pub use map::*;
|
||||||
|
|
||||||
// mod comparer;
|
mod set_compare;
|
||||||
// pub use comparer::*;
|
pub use set_compare::*;
|
||||||
|
|
||||||
mod memo;
|
mod memo;
|
||||||
pub use memo::*;
|
pub use memo::*;
|
||||||
|
|
99
packages/signals/src/set_compare.rs
Normal file
99
packages/signals/src/set_compare.rs
Normal file
|
@ -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(¤t).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> {}
|
Loading…
Add table
Reference in a new issue