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}