dioxus/packages/hooks/tests/memo.rs
Evan Almloff b6dc2a2230
Deduplicate reactive scope updates/Reset subscriptions on reruns/fix use memo double update (#2506)
* deduplicate effect updates

* only subscribe to signals read in the current run of reactive scopes

* subscribe to memo reads after recomputing the value
2024-06-18 18:49:25 -07:00

122 lines
3.5 KiB
Rust

#[tokio::test]
async fn memo_updates() {
use std::cell::RefCell;
use dioxus::prelude::*;
thread_local! {
static VEC_SIGNAL: RefCell<Option<Signal<Vec<usize>, SyncStorage>>> = const { RefCell::new(None) };
}
fn app() -> Element {
let mut vec = use_signal_sync(|| vec![0, 1, 2]);
// Signals should update if they are changed from another thread
use_hook(|| {
VEC_SIGNAL.with(|cell| {
*cell.borrow_mut() = Some(vec);
});
std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_millis(100));
vec.push(5);
});
});
let len = vec.len();
let len_memo = use_memo(move || vec.len());
// Make sure memos that update in the middle of a component work
if generation() < 2 {
vec.push(len);
}
// The memo should always be up to date
assert_eq!(vec.len(), len_memo());
rsx! {
for i in 0..len {
Child { index: i, vec }
}
}
}
#[component]
fn Child(index: usize, vec: Signal<Vec<usize>, SyncStorage>) -> Element {
// This memo should not rerun after the element is removed
let item = use_memo(move || vec.read()[index]);
rsx! {
div { "Item: {item}" }
}
}
let race = async move {
let mut dom = VirtualDom::new(app);
dom.rebuild_in_place();
let mut signal = VEC_SIGNAL.with(|cell| (*cell.borrow()).unwrap());
// Wait for the signal to update
for _ in 0..2 {
dom.wait_for_work().await;
dom.render_immediate(&mut dioxus::dioxus_core::NoOpMutations);
}
assert_eq!(signal(), vec![0, 1, 2, 3, 4, 5]);
// Remove each element from the vec
for _ in 0..6 {
signal.pop();
dom.wait_for_work().await;
dom.render_immediate(&mut dioxus::dioxus_core::NoOpMutations);
println!("Signal: {signal:?}");
}
};
tokio::select! {
_ = race => {},
_ = tokio::time::sleep(std::time::Duration::from_millis(1000)) => panic!("timed out")
};
}
#[tokio::test]
async fn use_memo_only_triggers_one_update() {
use dioxus::prelude::*;
use std::cell::RefCell;
thread_local! {
static VEC_SIGNAL: RefCell<Vec<usize>> = const { RefCell::new(Vec::new()) };
}
fn app() -> Element {
let mut count = use_signal(|| 0);
let memorized = use_memo(move || dbg!(count() * 2));
use_memo(move || {
println!("reading doubled");
let doubled = memorized();
VEC_SIGNAL.with_borrow_mut(|v| v.push(doubled))
});
// Writing to count many times in a row should not cause the memo to update other subscribers multiple times
use_hook(move || {
for _ in 0..10 {
count += 1;
// Reading the memo each time will trigger the memo to rerun immediately, but the VEC_SIGNAL should still only rerun once
println!("doubled {memorized}");
}
});
rsx! {}
}
let mut dom = VirtualDom::new(app);
dom.rebuild_in_place();
tokio::select! {
_ = dom.wait_for_work() => {},
_ = tokio::time::sleep(std::time::Duration::from_millis(100)) => {}
};
dom.render_immediate(&mut dioxus::dioxus_core::NoOpMutations);
assert_eq!(VEC_SIGNAL.with(|v| v.borrow().clone()), vec![0, 20]);
}