Struct leptos::Memo

source ·
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:

  1. The memo will only run once per change, no matter how many times you access its value.
  2. 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 an async 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>

source

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...
});
source

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> Clone for Memo<T>
where T: 'static,

source§

fn clone(&self) -> Memo<T>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<T> Debug for Memo<T>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
source§

impl<T> From<Memo<Option<T>>> for MaybeProp<T>

source§

fn from(value: Memo<Option<T>>) -> MaybeProp<T>

Converts to this type from the input type.
source§

impl<T> From<Memo<T>> for MaybeProp<T>
where T: Clone,

source§

fn from(value: Memo<T>) -> MaybeProp<T>

Converts to this type from the input type.
source§

impl<T> From<Memo<T>> for MaybeSignal<T>

source§

fn from(value: Memo<T>) -> MaybeSignal<T>

Converts to this type from the input type.
source§

impl<T> From<Memo<T>> for Signal<T>

source§

fn from(value: Memo<T>) -> Signal<T>

Converts to this type from the input type.
source§

impl<T> IntoAttribute for Memo<T>
where T: IntoAttribute + Clone,

source§

fn into_attribute(self) -> Attribute

Converts the object into an Attribute.
source§

fn into_attribute_boxed(self: Box<Memo<T>>) -> Attribute

Helper function for dealing with Box<dyn IntoAttribute>.
source§

impl IntoClass for Memo<bool>

source§

fn into_class(self) -> Class

Converts the object into a Class.
source§

fn into_class_boxed(self: Box<Memo<bool>>) -> Class

Helper function for dealing with Box<dyn IntoClass>.
source§

impl<T> IntoProperty for Memo<T>
where T: Into<JsValue> + Clone,

source§

fn into_property(self) -> Property

Converts the object into a Property.
source§

fn into_property_boxed(self: Box<Memo<T>>) -> Property

Helper function for dealing with Box<dyn IntoProperty>.
source§

impl<T> IntoStyle for Memo<T>
where T: IntoStyle + Clone,

source§

fn into_style(self) -> Style

Converts the object into a Style.
source§

fn into_style_boxed(self: Box<Memo<T>>) -> Style

Helper function for dealing with Box<dyn IntoStyle>.
source§

impl<T> IntoView for Memo<T>
where T: IntoView + Clone,

source§

fn into_view(self) -> View

Converts the value into View.
source§

impl<T> PartialEq for Memo<T>

source§

fn eq(&self, other: &Memo<T>) -> bool

This method tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
source§

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,

Serialize this value into the given Serde serializer. Read more
source§

impl<T> SignalDispose for Memo<T>

source§

fn dispose(self)

Disposes of the signal. This: Read more
source§

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);
§

type Value = T

The value held by the signal.
source§

fn get(&self) -> T

Clones and returns the current value of the signal, and subscribes the running effect to this signal. Read more
source§

fn try_get(&self) -> Option<T>

Clones and returns the signal value, returning Some if the signal is still alive, and None otherwise.
source§

impl<T> SignalGetUntracked for Memo<T>
where T: Clone,

§

type Value = T

The value held by the signal.
source§

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>

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,

source§

fn to_stream(&self) -> Pin<Box<dyn Stream<Item = T>>>

Generates a Stream that emits the new value of the signal whenever it changes. Read more
source§

impl<T> SignalWith for Memo<T>

§

type Value = T

The value held by the signal.
source§

fn with<O>(&self, f: impl FnOnce(&T) -> O) -> O

Applies a function to the current value of the signal, and subscribes the running effect to this signal. Read more
source§

fn try_with<O>(&self, f: impl FnOnce(&T) -> O) -> Option<O>

Applies a function to the current value of the signal, and subscribes the running effect to this signal. Returns Some if the signal is valid and the function ran, otherwise returns None.
source§

fn track(&self)

Subscribes to this signal in the current reactive scope without doing anything with its value.
source§

impl<T> SignalWithUntracked for Memo<T>

§

type Value = T

The value held by the signal.
source§

fn with_untracked<O>(&self, f: impl FnOnce(&T) -> O) -> O

Runs the provided closure with a reference to the current value without creating a dependency on the current scope. Read more
source§

fn try_with_untracked<O>(&self, f: impl FnOnce(&T) -> O) -> Option<O>

Runs the provided closure with a reference to the current value without creating a dependency on the current scope. Returns [Some(O)] if the signal is still valid, None otherwise.
source§

impl<T> Copy for Memo<T>

source§

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> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> IntoEither for T

source§

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 more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

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 more
source§

impl<CustErr, T, Request> IntoReq<Cbor, Request, CustErr> for T
where Request: ClientReq<CustErr>, T: Serialize + Send,

source§

fn into_req( self, path: &str, accepts: &str ) -> Result<Request, ServerFnError<CustErr>>

Attempts to serialize the arguments into an HTTP request.
source§

impl<CustErr, T, Request> IntoReq<GetUrl, Request, CustErr> for T
where Request: ClientReq<CustErr>, T: Serialize + Send,

source§

fn into_req( self, path: &str, accepts: &str ) -> Result<Request, ServerFnError<CustErr>>

Attempts to serialize the arguments into an HTTP request.
source§

impl<CustErr, T, Request> IntoReq<Json, Request, CustErr> for T
where Request: ClientReq<CustErr>, T: Serialize + Send,

source§

fn into_req( self, path: &str, accepts: &str ) -> Result<Request, ServerFnError<CustErr>>

Attempts to serialize the arguments into an HTTP request.
source§

impl<CustErr, T, Request> IntoReq<PostUrl, Request, CustErr> for T
where Request: ClientReq<CustErr>, T: Serialize + Send,

source§

fn into_req( self, path: &str, accepts: &str ) -> Result<Request, ServerFnError<CustErr>>

Attempts to serialize the arguments into an HTTP request.
source§

impl<CustErr, T, Response> IntoRes<Cbor, Response, CustErr> for T
where Response: Res<CustErr>, T: Serialize + Send,

source§

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
where Response: Res<CustErr>, T: Serialize + Send,

source§

async fn into_res(self) -> Result<Response, ServerFnError<CustErr>>

Attempts to serialize the output into an HTTP response.
source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
source§

impl<El> ElementDescriptorBounds for El
where El: Debug,