floem_reactive/
memo.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
use std::marker::PhantomData;

use crate::{
    effect::create_effect,
    read::{SignalRead, SignalTrack},
    scope::Scope,
    signal::{create_signal, ReadSignal},
    SignalGet, SignalUpdate, SignalWith,
};

/// Memo computes the value from the closure on creation, and stores the value.
///
/// It will act like a Signal when the value is different with the computed value
/// from last run, i.e., it will trigger a effect run when you Get() it whenever the
/// computed value changes to a different value.
pub struct Memo<T> {
    getter: ReadSignal<T>,
    ty: PhantomData<T>,
}

impl<T> Copy for Memo<T> {}

impl<T> Clone for Memo<T> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<T: Clone> SignalGet<T> for Memo<T> {
    fn id(&self) -> crate::id::Id {
        self.getter.id
    }
}

impl<T> SignalWith<T> for Memo<T> {
    fn id(&self) -> crate::id::Id {
        self.getter.id
    }
}
impl<T> SignalTrack<T> for Memo<T> {
    fn id(&self) -> crate::id::Id {
        self.getter.id
    }
}

/// Create a Memo which takes the computed value of the given function, and triggers
/// the reactive system when the computed value is different with the last computed value.
pub fn create_memo<T>(f: impl Fn(Option<&T>) -> T + 'static) -> Memo<T>
where
    T: PartialEq + 'static,
{
    let cx = Scope::current();
    let initial = f(None);
    let (getter, setter) = create_signal(initial);
    let reader = getter.read_untracked();

    create_effect(move |_| {
        cx.track();
        let (is_different, new_value) = {
            let last_value = reader.borrow();
            let new_value = f(Some(&last_value));
            (new_value != *last_value, new_value)
        };
        if is_different {
            setter.set(new_value);
        }
    });

    Memo {
        getter,
        ty: PhantomData,
    }
}