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
11pub 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
47pub 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}