use crate::{Hook, Hooks};
use core::hash::{Hash, Hasher};
use std::hash::DefaultHasher;
mod private {
pub trait Sealed {}
impl Sealed for crate::Hooks<'_, '_> {}
}
pub trait UseMemo: private::Sealed {
fn use_memo<F, D, T>(&mut self, f: F, deps: D) -> T
where
F: FnOnce() -> T,
D: Hash,
T: Clone + Send + Unpin + 'static;
}
fn hash_deps<D: Hash>(deps: D) -> u64 {
let mut hasher = DefaultHasher::new();
deps.hash(&mut hasher);
hasher.finish()
}
impl UseMemo for Hooks<'_, '_> {
fn use_memo<F, D, T>(&mut self, f: F, deps: D) -> T
where
F: FnOnce() -> T,
D: Hash,
T: Clone + Send + Unpin + 'static,
{
let deps_hash = hash_deps(deps);
let hook = self.use_hook(UseMemoImpl::<T>::default);
if hook.memoized_value.is_none() || hook.deps_hash != deps_hash {
hook.memoized_value = Some(f());
hook.deps_hash = deps_hash;
}
hook.memoized_value.clone().unwrap()
}
}
struct UseMemoImpl<T> {
deps_hash: u64,
memoized_value: Option<T>,
}
impl<T> Default for UseMemoImpl<T> {
fn default() -> Self {
Self {
deps_hash: 0,
memoized_value: None,
}
}
}
impl<T: Send + Unpin> Hook for UseMemoImpl<T> {}
#[cfg(test)]
mod tests {
use crate::prelude::*;
#[derive(Clone)]
struct MyStruct(u64);
#[component]
fn MyComponent(mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
let s = "foo bar";
let counter = hooks.use_state(|| 0);
let a = hooks.use_memo(|| 42, (counter, s));
let b = hooks.use_memo(|| MyStruct(1), (counter, s));
let c = hooks.use_memo(|| "hello!", (counter, s));
hooks.use_memo(|| {}, (counter, s));
assert_eq!(a, 42);
assert_eq!(b.0, 1);
assert_eq!(c, "hello!");
element!(View)
}
#[test]
fn test_use_memo() {
let s = element!(MyComponent).to_string();
assert_eq!(s, "");
}
}