mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
Make memos lazy
This commit is contained in:
parent
3e4adcad21
commit
eb4d5abff9
3 changed files with 101 additions and 54 deletions
|
@ -90,10 +90,16 @@ where
|
|||
if let Some(transition) = self.runtime.running_transition() {
|
||||
todo!()
|
||||
} else {
|
||||
self.runtime
|
||||
.memo((self.scope, self.id), |memo_state: &MemoState<T>| {
|
||||
f(&memo_state.value.borrow())
|
||||
})
|
||||
self.runtime.memo(
|
||||
(self.scope, self.id),
|
||||
|memo_state: &MemoState<T>| match &*memo_state.value.borrow() {
|
||||
Some(v) => f(v),
|
||||
None => {
|
||||
memo_state.run((self.scope, self.id));
|
||||
f(memo_state.value.borrow().as_ref().unwrap())
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,7 +120,7 @@ where
|
|||
{
|
||||
runtime: &'static Runtime,
|
||||
f: Box<RefCell<dyn FnMut(Option<&T>) -> T>>,
|
||||
value: RefCell<T>,
|
||||
value: RefCell<Option<T>>,
|
||||
t_value: RefCell<Option<T>>,
|
||||
sources: RefCell<HashSet<Source>>,
|
||||
subscribers: RefCell<HashSet<Subscriber>>,
|
||||
|
@ -148,12 +154,11 @@ where
|
|||
{
|
||||
pub fn new(runtime: &'static Runtime, f: impl FnMut(Option<&T>) -> T + 'static) -> Self {
|
||||
let f = Box::new(RefCell::new(f));
|
||||
let value = (f.borrow_mut())(None);
|
||||
|
||||
Self {
|
||||
runtime,
|
||||
f,
|
||||
value: RefCell::new(value),
|
||||
value: RefCell::new(None),
|
||||
sources: Default::default(),
|
||||
t_value: Default::default(),
|
||||
subscribers: Default::default(),
|
||||
|
@ -196,8 +201,8 @@ where
|
|||
self.runtime.push_stack(Subscriber::Memo(id));
|
||||
|
||||
// actually run the effect
|
||||
let v = { (self.f.borrow_mut())(Some(&self.value.borrow())) };
|
||||
*self.value.borrow_mut() = v;
|
||||
let v = { (self.f.borrow_mut())(self.value.borrow().as_ref()) };
|
||||
*self.value.borrow_mut() = Some(v);
|
||||
|
||||
// notify subscribers
|
||||
let subs = { self.subscribers.borrow().clone() };
|
||||
|
|
87
leptos_reactive/tests/memo.rs
Normal file
87
leptos_reactive/tests/memo.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
use leptos_reactive::create_scope;
|
||||
|
||||
#[test]
|
||||
fn basic_memo() {
|
||||
create_scope(|cx| {
|
||||
let a = cx.create_memo(|_| 5);
|
||||
assert_eq!(a(), 5);
|
||||
})
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn memo_with_computed_value() {
|
||||
create_scope(|cx| {
|
||||
let (a, set_a) = cx.create_signal(0);
|
||||
let (b, set_b) = cx.create_signal(0);
|
||||
let c = cx.create_memo(move |_| a() + b());
|
||||
assert_eq!(c(), 0);
|
||||
set_a(|a| *a = 5);
|
||||
assert_eq!(c(), 5);
|
||||
set_b(|b| *b = 1);
|
||||
assert_eq!(c(), 6);
|
||||
})
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_memos() {
|
||||
create_scope(|cx| {
|
||||
let (a, set_a) = cx.create_signal(0);
|
||||
let (b, set_b) = cx.create_signal(0);
|
||||
let c = cx.create_memo(move |_| a() + b());
|
||||
let d = cx.create_memo(move |_| c() * 2);
|
||||
let e = cx.create_memo(move |_| d() + 1);
|
||||
assert_eq!(d(), 0);
|
||||
set_a(|a| *a = 5);
|
||||
assert_eq!(c(), 5);
|
||||
assert_eq!(d(), 10);
|
||||
assert_eq!(e(), 11);
|
||||
set_b(|b| *b = 1);
|
||||
assert_eq!(c(), 6);
|
||||
assert_eq!(d(), 12);
|
||||
assert_eq!(e(), 13);
|
||||
})
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn memo_runs_only_when_inputs_change() {
|
||||
use std::{cell::Cell, rc::Rc};
|
||||
|
||||
create_scope(|cx| {
|
||||
let call_count = Rc::new(Cell::new(0));
|
||||
let (a, set_a) = cx.create_signal(0);
|
||||
let (b, set_b) = cx.create_signal(0);
|
||||
let (c, set_c) = cx.create_signal(0);
|
||||
|
||||
// pretend that this is some kind of expensive computation and we need to access its its value often
|
||||
// we could do this with a derived signal, but that would re-run the computation
|
||||
// memos should only run when their inputs actually change: this is the only point
|
||||
let c = cx.create_memo({
|
||||
let call_count = call_count.clone();
|
||||
move |_| {
|
||||
call_count.set(call_count.get() + 1);
|
||||
a() + b() + c()
|
||||
}
|
||||
});
|
||||
|
||||
assert_eq!(call_count.get(), 1);
|
||||
|
||||
// here we access the value a bunch of times
|
||||
assert_eq!(c(), 0);
|
||||
assert_eq!(c(), 0);
|
||||
assert_eq!(c(), 0);
|
||||
assert_eq!(c(), 0);
|
||||
assert_eq!(c(), 0);
|
||||
|
||||
// we've still only called the memo calculation once
|
||||
assert_eq!(call_count.get(), 1);
|
||||
|
||||
// and we only call it again when an input changes
|
||||
set_a(|n| *n = 1);
|
||||
assert_eq!(c(), 1);
|
||||
assert_eq!(call_count.get(), 2);
|
||||
})
|
||||
.dispose()
|
||||
}
|
|
@ -25,48 +25,3 @@ fn derived_signals() {
|
|||
})
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_memo() {
|
||||
create_scope(|cx| {
|
||||
let a = cx.create_memo(|_| 5);
|
||||
assert_eq!(a(), 5);
|
||||
})
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn memo_with_computed_value() {
|
||||
create_scope(|cx| {
|
||||
let (a, set_a) = cx.create_signal(0);
|
||||
let (b, set_b) = cx.create_signal(0);
|
||||
let c = cx.create_memo(move |_| a() + b());
|
||||
assert_eq!(c(), 0);
|
||||
set_a(|a| *a = 5);
|
||||
assert_eq!(c(), 5);
|
||||
set_b(|b| *b = 1);
|
||||
assert_eq!(c(), 6);
|
||||
})
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_memos() {
|
||||
create_scope(|cx| {
|
||||
let (a, set_a) = cx.create_signal(0);
|
||||
let (b, set_b) = cx.create_signal(0);
|
||||
let c = cx.create_memo(move |_| a() + b());
|
||||
let d = cx.create_memo(move |_| c() * 2);
|
||||
let e = cx.create_memo(move |_| d() + 1);
|
||||
assert_eq!(d(), 0);
|
||||
set_a(|a| *a = 5);
|
||||
assert_eq!(c(), 5);
|
||||
assert_eq!(d(), 10);
|
||||
assert_eq!(e(), 11);
|
||||
set_b(|b| *b = 1);
|
||||
assert_eq!(c(), 6);
|
||||
assert_eq!(d(), 12);
|
||||
assert_eq!(e(), 13);
|
||||
})
|
||||
.dispose()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue