use std::marker::PhantomData;
use crate::{
effect::create_effect,
read::{SignalRead, SignalTrack},
scope::Scope,
signal::{create_signal, ReadSignal},
SignalGet, SignalUpdate, SignalWith,
};
pub struct Memo<T> {
getter: ReadSignal<T>,
ty: PhantomData<T>,
}
impl<T> Copy for Memo<T> {}
impl<T> Clone for Memo<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: Clone> SignalGet<T> for Memo<T> {
fn id(&self) -> crate::id::Id {
self.getter.id
}
}
impl<T> SignalWith<T> for Memo<T> {
fn id(&self) -> crate::id::Id {
self.getter.id
}
}
impl<T> SignalTrack<T> for Memo<T> {
fn id(&self) -> crate::id::Id {
self.getter.id
}
}
pub fn create_memo<T>(f: impl Fn(Option<&T>) -> T + 'static) -> Memo<T>
where
T: PartialEq + 'static,
{
let cx = Scope::current();
let initial = f(None);
let (getter, setter) = create_signal(initial);
let reader = getter.read_untracked();
create_effect(move |_| {
cx.track();
let (is_different, new_value) = {
let last_value = reader.borrow();
let new_value = f(Some(&last_value));
(new_value != *last_value, new_value)
};
if is_different {
setter.set(new_value);
}
});
Memo {
getter,
ty: PhantomData,
}
}