floem_reactive/
memo.rs

1use std::marker::PhantomData;
2
3use crate::{
4    effect::create_effect,
5    read::{SignalRead, SignalTrack},
6    scope::Scope,
7    signal::{create_signal, NotThreadSafe, ReadSignal},
8    SignalGet, SignalUpdate, SignalWith,
9};
10
11/// Memo computes the value from the closure on creation, and stores the value.
12///
13/// It will act like a Signal when the value is different with the computed value
14/// from last run, i.e., it will trigger a effect run when you Get() it whenever the
15/// computed value changes to a different value.
16pub struct Memo<T> {
17    getter: ReadSignal<T>,
18    ty: PhantomData<T>,
19    pub(crate) ts: PhantomData<NotThreadSafe>,
20}
21
22impl<T> Copy for Memo<T> {}
23
24impl<T> Clone for Memo<T> {
25    fn clone(&self) -> Self {
26        *self
27    }
28}
29
30impl<T: Clone> SignalGet<T> for Memo<T> {
31    fn id(&self) -> crate::id::Id {
32        self.getter.id
33    }
34}
35
36impl<T> SignalWith<T> for Memo<T> {
37    fn id(&self) -> crate::id::Id {
38        self.getter.id
39    }
40}
41impl<T> SignalTrack<T> for Memo<T> {
42    fn id(&self) -> crate::id::Id {
43        self.getter.id
44    }
45}
46
47/// Create a Memo which takes the computed value of the given function, and triggers
48/// the reactive system when the computed value is different with the last computed value.
49pub fn create_memo<T>(f: impl Fn(Option<&T>) -> T + 'static) -> Memo<T>
50where
51    T: PartialEq + 'static,
52{
53    let cx = Scope::current();
54    let initial = f(None);
55    let (getter, setter) = create_signal(initial);
56    let reader = getter.read_untracked();
57
58    create_effect(move |_| {
59        cx.track();
60        let (is_different, new_value) = {
61            let last_value = reader.borrow();
62            let new_value = f(Some(&last_value));
63            (new_value != *last_value, new_value)
64        };
65        if is_different {
66            setter.set(new_value);
67        }
68    });
69
70    Memo {
71        getter,
72        ty: PhantomData,
73        ts: PhantomData,
74    }
75}