dioxus_signals/
set_compare.rs

1use crate::write::Writable;
2use std::hash::Hash;
3
4use crate::read::Readable;
5use dioxus_core::prelude::*;
6use futures_util::StreamExt;
7use generational_box::{Storage, UnsyncStorage};
8
9use crate::{CopyValue, ReadOnlySignal, Signal, SignalData};
10use rustc_hash::FxHashMap;
11
12/// An object that can efficiently compare a value to a set of values.
13#[derive(Debug)]
14pub struct SetCompare<R: 'static, S: Storage<SignalData<bool>> = UnsyncStorage> {
15    subscribers: CopyValue<FxHashMap<R, Signal<bool, S>>>,
16}
17
18impl<R: Eq + Hash> SetCompare<R> {
19    /// Creates a new [`SetCompare`] which efficiently tracks when a value changes to check if it is equal to a set of values.
20    ///
21    /// 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.
22    #[track_caller]
23    pub fn new(f: impl FnMut() -> R + 'static) -> SetCompare<R> {
24        Self::new_maybe_sync(f)
25    }
26}
27
28impl<R: Eq + Hash, S: Storage<SignalData<bool>>> SetCompare<R, S> {
29    /// 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.
30    ///
31    /// 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.
32    #[track_caller]
33    pub fn new_maybe_sync(mut f: impl FnMut() -> R + 'static) -> SetCompare<R> {
34        let subscribers: CopyValue<FxHashMap<R, Signal<bool>>> =
35            CopyValue::new(FxHashMap::default());
36        let mut previous = CopyValue::new(None);
37
38        let mut recompute = move || {
39            let subscribers = subscribers.read();
40            let mut previous = previous.write();
41
42            if let Some(previous) = previous.take() {
43                if let Some(mut value) = subscribers.get(&previous).cloned() {
44                    *value.write() = false;
45                }
46            }
47
48            let current = f();
49
50            if let Some(mut value) = subscribers.get(&current).cloned() {
51                *value.write() = true;
52            }
53
54            *previous = Some(current);
55        };
56        let (rc, mut changed) = ReactiveContext::new();
57        spawn(async move {
58            loop {
59                // Recompute the value
60                rc.reset_and_run_in(&mut recompute);
61
62                // Wait for context to change
63                let _ = changed.next().await;
64            }
65        });
66
67        SetCompare { subscribers }
68    }
69
70    /// Returns a signal which is true when the value is equal to the value passed to this function.
71    pub fn equal(&mut self, value: R) -> ReadOnlySignal<bool, S> {
72        let subscribers = self.subscribers.write();
73
74        match subscribers.get(&value) {
75            Some(&signal) => signal.into(),
76            None => {
77                drop(subscribers);
78                let mut subscribers = self.subscribers.write();
79                let signal = Signal::new_maybe_sync(false);
80                subscribers.insert(value, signal);
81                signal.into()
82            }
83        }
84    }
85}
86
87impl<R: 'static, S: Storage<SignalData<bool>>> PartialEq for SetCompare<R, S> {
88    fn eq(&self, other: &Self) -> bool {
89        self.subscribers == other.subscribers
90    }
91}
92
93impl<R, S: Storage<SignalData<bool>>> Clone for SetCompare<R, S> {
94    fn clone(&self) -> Self {
95        *self
96    }
97}
98
99impl<R, S: Storage<SignalData<bool>>> Copy for SetCompare<R, S> {}