pub struct Memo<T>where
T: 'static,{ /* private fields */ }
Expand description
An efficient derived reactive value based on other reactive values.
Unlike a “derived signal,” a memo comes with two guarantees:
- The memo will only run once per change, no matter how many times you access its value.
- The memo will only notify its dependents if the value of the computation changes.
This makes a memo the perfect tool for expensive computations.
Memos have a certain overhead compared to derived signals. In most cases, you should create a derived signal. But if the derivation calculation is expensive, you should create a memo.
As with create_effect
, the argument to the memo function is the previous value,
i.e., the current value of the memo, which will be None
for the initial calculation.
§Core Trait Implementations
.get()
(or calling the signal as a function) clones the current value of the signal. If you call it within an effect, it will cause that effect to subscribe to the signal, and to re-run whenever the value of the signal changes..get_untracked()
clones the value of the signal without reactively tracking it.
.with()
allows you to reactively access the signal’s value without cloning by applying a callback function..with_untracked()
allows you to access the signal’s value without reactively tracking it.
.to_stream()
converts the signal to anasync
stream of values.
§Examples
let (value, set_value) = create_signal(0);
// 🆗 we could create a derived signal with a simple function
let double_value = move || value.get() * 2;
set_value.set(2);
assert_eq!(double_value(), 4);
// but imagine the computation is really expensive
let expensive = move || really_expensive_computation(value.get()); // lazy: doesn't run until called
create_effect(move |_| {
// 🆗 run #1: calls `really_expensive_computation` the first time
log::debug!("expensive = {}", expensive());
});
create_effect(move |_| {
// ❌ run #2: this calls `really_expensive_computation` a second time!
let value = expensive();
// do something else...
});
// instead, we create a memo
// 🆗 run #1: the calculation runs once immediately
let memoized = create_memo(move |_| really_expensive_computation(value.get()));
create_effect(move |_| {
// 🆗 reads the current value of the memo
log::debug!("memoized = {}", memoized.get());
});
create_effect(move |_| {
// ✅ reads the current value **without re-running the calculation**
// can be `memoized()` on nightly
let value = memoized.get();
// do something else...
});
Implementations§
source§impl<T> Memo<T>
impl<T> Memo<T>
sourcepub fn new(f: impl Fn(Option<&T>) -> T + 'static) -> Memo<T>where
T: PartialEq + 'static,
pub fn new(f: impl Fn(Option<&T>) -> T + 'static) -> Memo<T>where
T: PartialEq + 'static,
Creates a new memo from the given function.
This is identical to create_memo
.
let value = RwSignal::new(0);
// 🆗 we could create a derived signal with a simple function
let double_value = move || value.get() * 2;
value.set(2);
assert_eq!(double_value(), 4);
// but imagine the computation is really expensive
let expensive = move || really_expensive_computation(value.get()); // lazy: doesn't run until called
Effect::new(move |_| {
// 🆗 run #1: calls `really_expensive_computation` the first time
log::debug!("expensive = {}", expensive());
});
Effect::new(move |_| {
// ❌ run #2: this calls `really_expensive_computation` a second time!
let value = expensive();
// do something else...
});
// instead, we create a memo
// 🆗 run #1: the calculation runs once immediately
let memoized = Memo::new(move |_| really_expensive_computation(value.get()));
Effect::new(move |_| {
// 🆗 reads the current value of the memo
// can be `memoized()` on nightly
log::debug!("memoized = {}", memoized.get());
});
Effect::new(move |_| {
// ✅ reads the current value **without re-running the calculation**
let value = memoized.get();
// do something else...
});
sourcepub fn new_owning(f: impl Fn(Option<T>) -> (T, bool) + 'static) -> Memo<T>where
T: 'static,
pub fn new_owning(f: impl Fn(Option<T>) -> (T, bool) + 'static) -> Memo<T>where
T: 'static,
Creates a new owning memo from the given function.
This is identical to create_owning_memo
.
pub struct State {
name: String,
token: String,
}
let state = RwSignal::new(State {
name: "Alice".to_owned(),
token: "abcdef".to_owned(),
});
// If we used `Memo::new`, we'd need to allocate every time the state changes, but by using
// `Memo::new_owning` we can allocate only when `state.name` changes.
let name = Memo::new_owning(move |old_name| {
state.with(move |state| {
if let Some(name) =
old_name.filter(|old_name| old_name == &state.name)
{
(name, false)
} else {
(state.name.clone(), true)
}
})
});
let set_name = move |name| state.update(|state| state.name = name);
// We can also re-use the last allocation even when the value changes, which is usually faster,
// but may have some caveats (e.g. if the value size is drastically reduced, the memory will
// still be used for the life of the memo).
let token = Memo::new_owning(move |old_token| {
state.with(move |state| {
let is_different = old_token.as_ref() != Some(&state.token);
let mut token = old_token.unwrap_or_else(String::new);
if is_different {
token.clone_from(&state.token);
}
(token, is_different)
})
});
let set_token = move |new_token| state.update(|state| state.token = new_token);
Trait Implementations§
source§impl<T> From<Memo<T>> for MaybeSignal<T>
impl<T> From<Memo<T>> for MaybeSignal<T>
source§fn from(value: Memo<T>) -> MaybeSignal<T>
fn from(value: Memo<T>) -> MaybeSignal<T>
Converts to this type from the input type.
source§impl<T> IntoAttribute for Memo<T>where
T: IntoAttribute + Clone,
impl<T> IntoAttribute for Memo<T>where
T: IntoAttribute + Clone,
source§impl<T> IntoProperty for Memo<T>
impl<T> IntoProperty for Memo<T>
source§impl<T> PartialEq for Memo<T>
impl<T> PartialEq for Memo<T>
source§impl<T> Serialize for Memo<T>where
T: Serialize,
impl<T> Serialize for Memo<T>where
T: Serialize,
source§fn serialize<S>(
&self,
serializer: S
) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>where
S: Serializer,
fn serialize<S>(
&self,
serializer: S
) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>where
S: Serializer,
Serialize this value into the given Serde serializer. Read more
source§impl<T> SignalDispose for Memo<T>
impl<T> SignalDispose for Memo<T>
source§impl<T> SignalGet for Memo<T>where
T: Clone,
impl<T> SignalGet for Memo<T>where
T: Clone,
§Examples
let (count, set_count) = create_signal(0);
let double_count = create_memo(move |_| count.get() * 2);
assert_eq!(double_count.get(), 0);
set_count.set(1);
// can be `double_count()` on nightly
// assert_eq!(double_count(), 2);
assert_eq!(double_count.get(), 2);
source§impl<T> SignalGetUntracked for Memo<T>where
T: Clone,
impl<T> SignalGetUntracked for Memo<T>where
T: Clone,
source§fn get_untracked(&self) -> T
fn get_untracked(&self) -> T
Gets the signal’s value without creating a dependency on the
current scope. Read more
source§fn try_get_untracked(&self) -> Option<T>
fn try_get_untracked(&self) -> Option<T>
Gets the signal’s value without creating a dependency on the
current scope. Returns [
Some(T)
] if the signal is still
valid, None
otherwise.source§impl<T> SignalStream<T> for Memo<T>where
T: Clone,
impl<T> SignalStream<T> for Memo<T>where
T: Clone,
source§impl<T> SignalWith for Memo<T>
impl<T> SignalWith for Memo<T>
source§impl<T> SignalWithUntracked for Memo<T>
impl<T> SignalWithUntracked for Memo<T>
impl<T> Copy for Memo<T>
impl<T> Eq for Memo<T>
Auto Trait Implementations§
impl<T> Freeze for Memo<T>
impl<T> RefUnwindSafe for Memo<T>where
T: RefUnwindSafe,
impl<T> Send for Memo<T>where
T: Send,
impl<T> Sync for Memo<T>where
T: Sync,
impl<T> Unpin for Memo<T>where
T: Unpin,
impl<T> UnwindSafe for Memo<T>where
T: UnwindSafe,
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
Compare self to
key
and return true
if they are equal.source§impl<T> Instrument for T
impl<T> Instrument for T
source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
source§impl<T> IntoEither for T
impl<T> IntoEither for T
source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
Converts
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moresource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
Converts
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moresource§impl<CustErr, T, Response> IntoRes<Cbor, Response, CustErr> for T
impl<CustErr, T, Response> IntoRes<Cbor, Response, CustErr> for T
source§async fn into_res(self) -> Result<Response, ServerFnError<CustErr>>
async fn into_res(self) -> Result<Response, ServerFnError<CustErr>>
Attempts to serialize the output into an HTTP response.
source§impl<CustErr, T, Response> IntoRes<Json, Response, CustErr> for T
impl<CustErr, T, Response> IntoRes<Json, Response, CustErr> for T
source§async fn into_res(self) -> Result<Response, ServerFnError<CustErr>>
async fn into_res(self) -> Result<Response, ServerFnError<CustErr>>
Attempts to serialize the output into an HTTP response.