Skip to main content

react_rs_core/
memo.rs

1use crate::effect::create_effect;
2use crate::signal::{create_signal, ReadSignal};
3
4pub struct Memo<T> {
5    read: ReadSignal<T>,
6}
7
8impl<T: Clone> Memo<T> {
9    pub fn get(&self) -> T {
10        self.read.get()
11    }
12
13    pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
14        self.read.with(f)
15    }
16}
17
18impl<T> Clone for Memo<T> {
19    fn clone(&self) -> Self {
20        Self {
21            read: self.read.clone(),
22        }
23    }
24}
25
26pub fn create_memo<T, F>(f: F) -> Memo<T>
27where
28    F: Fn() -> T + 'static,
29    T: PartialEq + Clone + 'static,
30{
31    let initial = f();
32    let (read, write) = create_signal(initial);
33
34    create_effect(move || {
35        let new_value = f();
36        write.update(|current| {
37            if *current != new_value {
38                *current = new_value;
39            }
40        });
41    });
42
43    Memo { read }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use crate::signal::create_signal;
50    use std::cell::RefCell;
51    use std::rc::Rc;
52
53    #[test]
54    fn test_memo_basic() {
55        let (count, set_count) = create_signal(2);
56        let doubled = create_memo(move || count.get() * 2);
57
58        assert_eq!(doubled.get(), 4);
59
60        set_count.set(5);
61        assert_eq!(doubled.get(), 10);
62    }
63
64    #[test]
65    fn test_memo_caching() {
66        let (count, set_count) = create_signal(1);
67        let compute_count = Rc::new(RefCell::new(0));
68        let compute_count_clone = compute_count.clone();
69
70        let doubled = create_memo(move || {
71            *compute_count_clone.borrow_mut() += 1;
72            count.get() * 2
73        });
74
75        assert_eq!(doubled.get(), 2);
76        assert_eq!(doubled.get(), 2);
77        let initial_count = *compute_count.borrow();
78
79        set_count.set(2);
80        assert_eq!(doubled.get(), 4);
81        assert_eq!(*compute_count.borrow(), initial_count + 1);
82    }
83
84    #[test]
85    fn test_memo_only_updates_on_change() {
86        let (count, set_count) = create_signal(1);
87        let compute_count = Rc::new(RefCell::new(0));
88        let compute_count_clone = compute_count.clone();
89
90        let is_even = create_memo(move || {
91            *compute_count_clone.borrow_mut() += 1;
92            count.get() % 2 == 0
93        });
94
95        assert!(!is_even.get());
96        let count_after_init = *compute_count.borrow();
97
98        set_count.set(3);
99        assert!(!is_even.get());
100        assert_eq!(*compute_count.borrow(), count_after_init + 1);
101
102        set_count.set(4);
103        assert!(is_even.get());
104        assert_eq!(*compute_count.borrow(), count_after_init + 2);
105    }
106
107    #[test]
108    fn test_memo_chain() {
109        let (count, set_count) = create_signal(1);
110
111        let doubled = {
112            let count = count.clone();
113            create_memo(move || count.get() * 2)
114        };
115
116        let quadrupled = {
117            let doubled = doubled.clone();
118            create_memo(move || doubled.get() * 2)
119        };
120
121        assert_eq!(count.get(), 1);
122        assert_eq!(doubled.get(), 2);
123        assert_eq!(quadrupled.get(), 4);
124
125        set_count.set(3);
126
127        assert_eq!(count.get(), 3);
128        assert_eq!(doubled.get(), 6);
129        assert_eq!(quadrupled.get(), 12);
130    }
131}