mirror of
https://github.com/leptos-rs/leptos
synced 2024-09-20 06:21:57 +00:00
docs: porting docs from 0.6 to 0.7
This commit is contained in:
parent
05d01141c5
commit
8f0a8e05b4
5 changed files with 145 additions and 0 deletions
|
@ -211,6 +211,7 @@ where
|
|||
I: Send + Sync + 'static,
|
||||
O: Send + Sync + 'static,
|
||||
{
|
||||
/// Calls the `async` function with a reference to the input type as its argument.
|
||||
#[track_caller]
|
||||
pub fn dispatch(&self, input: I) {
|
||||
if !is_suppressing_resource_load() {
|
||||
|
@ -261,6 +262,8 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Calls the `async` function with a reference to the input type as its argument,
|
||||
/// ensuring that it is spawned on the current thread.
|
||||
#[track_caller]
|
||||
pub fn dispatch_local(&self, input: I) {
|
||||
if !is_suppressing_resource_load() {
|
||||
|
@ -771,6 +774,7 @@ where
|
|||
inner.into()
|
||||
}
|
||||
|
||||
/// Calls the `async` function with a reference to the input type as its argument.
|
||||
#[track_caller]
|
||||
pub fn dispatch(&self, input: I) {
|
||||
self.inner.with_value(|inner| inner.dispatch(input));
|
||||
|
|
|
@ -690,6 +690,7 @@ impl<I, O> Clone for ArcSubmission<I, O> {
|
|||
}
|
||||
}
|
||||
|
||||
/// An action that has been submitted by dispatching it to a [`MultiAction`].
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Submission<I, O>
|
||||
where
|
||||
|
|
|
@ -18,6 +18,76 @@ use std::{
|
|||
sync::{Arc, RwLock, Weak},
|
||||
};
|
||||
|
||||
/// An efficient derived reactive value based on other reactive values.
|
||||
///
|
||||
/// This is a reference-counted memo, which is `Clone` but not `Copy`.
|
||||
/// For arena-allocated `Copy` memos, use [`Memo`].
|
||||
///
|
||||
/// 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 an [`Effect`](crate::effects::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()`](crate::traits::Get) clones the current value of the memo.
|
||||
/// If you call it within an effect, it will cause that effect to subscribe
|
||||
/// to the memo, and to re-run whenever the value of the memo changes.
|
||||
/// - [`.get_untracked()`](crate::traits::GetUntracked) clones the value of
|
||||
/// the memo without reactively tracking it.
|
||||
/// - [`.read()`](crate::traits::Read) returns a guard that allows accessing the
|
||||
/// value of the memo by reference. If you call it within an effect, it will
|
||||
/// cause that effect to subscribe to the memo, and to re-run whenever the
|
||||
/// value of the memo changes.
|
||||
/// - [`.read_untracked()`](crate::traits::ReadUntracked) gives access to the
|
||||
/// current value of the memo without reactively tracking it.
|
||||
/// - [`.with()`](crate::traits::With) allows you to reactively access the memo’s
|
||||
/// value without cloning by applying a callback function.
|
||||
/// - [`.with_untracked()`](crate::traits::WithUntracked) allows you to access
|
||||
/// the signal’s value by applying a callback function without reactively
|
||||
/// tracking it.
|
||||
/// - [`.to_stream()`](crate::traits::ToStream) converts the memo to an `async`
|
||||
/// stream of values.
|
||||
/// - [`::from_stream()`](crate::traits::FromStream) converts an `async` stream
|
||||
/// of values into a memo containing the latest value.
|
||||
///
|
||||
/// ## Examples
|
||||
/// ```
|
||||
/// # use reactive_graph::prelude::*;
|
||||
/// # use reactive_graph::computed::*;
|
||||
/// # use reactive_graph::signal::signal;
|
||||
/// # fn really_expensive_computation(value: i32) -> i32 { value };
|
||||
/// let (value, set_value) = 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
|
||||
/// // 🆗 run #1: calls `really_expensive_computation` the first time
|
||||
/// println!("expensive = {}", expensive());
|
||||
/// // ❌ run #2: this calls `really_expensive_computation` a second time!
|
||||
/// let some_value = expensive();
|
||||
///
|
||||
/// // instead, we create a memo
|
||||
/// // 🆗 run #1: the calculation runs once immediately
|
||||
/// let memoized = ArcMemo::new(move |_| really_expensive_computation(value.get()));
|
||||
/// // 🆗 reads the current value of the memo
|
||||
/// // can be `memoized()` on nightly
|
||||
/// println!("memoized = {}", memoized.get());
|
||||
/// // ✅ reads the current value **without re-running the calculation**
|
||||
/// let some_value = memoized.get();
|
||||
/// ```
|
||||
pub struct ArcMemo<T> {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: &'static Location<'static>,
|
||||
|
@ -25,6 +95,10 @@ pub struct ArcMemo<T> {
|
|||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> ArcMemo<T> {
|
||||
/// Creates a new memo by passing a function that computes the value.
|
||||
///
|
||||
/// This is lazy: the function will not be called until the memo's value is read for the first
|
||||
/// time.
|
||||
#[track_caller]
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
|
@ -37,6 +111,12 @@ impl<T: Send + Sync + 'static> ArcMemo<T> {
|
|||
Self::new_with_compare(fun, |lhs, rhs| lhs.as_ref() != rhs.as_ref())
|
||||
}
|
||||
|
||||
/// Creates a new memo by passing a function that computes the value, and a comparison function
|
||||
/// that takes the previous value and the new value and returns `true` if the value has
|
||||
/// changed.
|
||||
///
|
||||
/// This is lazy: the function will not be called until the memo's value is read for the first
|
||||
/// time.
|
||||
#[track_caller]
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
|
@ -56,6 +136,13 @@ impl<T: Send + Sync + 'static> ArcMemo<T> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Creates a new memo by passing a function that computes the value.
|
||||
///
|
||||
/// Unlike [`ArcMemo::new`](), this receives ownership of the previous value. As a result, it
|
||||
/// must return both the new value and a `bool` that is `true` if the value has changed.
|
||||
///
|
||||
/// This is lazy: the function will not be called until the memo's value is read for the first
|
||||
/// time.
|
||||
#[track_caller]
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
|
|
|
@ -32,6 +32,48 @@ use std::{
|
|||
task::Waker,
|
||||
};
|
||||
|
||||
/// A reactive value that is derived by running an asynchronous computation in response to changes
|
||||
/// in its sources.
|
||||
///
|
||||
/// When one of its dependencies changes, this will re-run its async computation, then notify other
|
||||
/// values that depend on it that it has changed.
|
||||
///
|
||||
/// ## Examples
|
||||
/// ```rust
|
||||
/// # use reactive_graph::computed::*;
|
||||
/// # use reactive_graph::signal::*;
|
||||
/// # use reactive_graph::prelude::*;
|
||||
/// # tokio_test::block_on(async move {
|
||||
/// # any_spawner::Executor::init_tokio();
|
||||
/// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
|
||||
///
|
||||
/// let signal1 = RwSignal::new(0);
|
||||
/// let signal2 = RwSignal::new(0);
|
||||
/// let derived = ArcAsyncDerived::new(move || async move {
|
||||
/// // reactive values can be tracked anywhere in the `async` block
|
||||
/// let value1 = signal1.get();
|
||||
/// tokio::time::sleep(std::time::Duration::from_millis(25)).await;
|
||||
/// let value2 = signal2.get();
|
||||
///
|
||||
/// value1 + value2
|
||||
/// });
|
||||
///
|
||||
/// // the value can be accessed synchronously as `Option<T>`
|
||||
/// assert_eq!(derived.get(), None);
|
||||
/// // we can also .await the value, i.e., convert it into a Future
|
||||
/// assert_eq!(derived.clone().await, 0);
|
||||
/// assert_eq!(derived.get(), Some(0));
|
||||
///
|
||||
/// signal1.set(1);
|
||||
/// // while the new value is still pending, the signal holds the old value
|
||||
/// tokio::time::sleep(std::time::Duration::from_millis(5)).await;
|
||||
/// assert_eq!(derived.get(), Some(0));
|
||||
///
|
||||
/// // setting multiple dependencies will hold until the latest change is ready
|
||||
/// signal2.set(1);
|
||||
/// assert_eq!(derived.clone().await, 2);
|
||||
/// # });
|
||||
/// ```
|
||||
pub struct ArcAsyncDerived<T> {
|
||||
#[cfg(debug_assertions)]
|
||||
pub(crate) defined_at: &'static Location<'static>,
|
||||
|
|
|
@ -18,6 +18,8 @@ use std::{
|
|||
};
|
||||
|
||||
pin_project! {
|
||||
/// A [`Future`] wrapper that sets the [`Owner`] and [`Observer`] before polling the inner
|
||||
/// `Future`.
|
||||
pub struct ScopedFuture<Fut> {
|
||||
owner: Option<Owner>,
|
||||
observer: Option<AnySubscriber>,
|
||||
|
@ -27,6 +29,8 @@ pin_project! {
|
|||
}
|
||||
|
||||
impl<Fut> ScopedFuture<Fut> {
|
||||
/// Wraps the given `Future` by taking the current [`Owner`] and [`Observer`] and re-setting
|
||||
/// them as the active owner and observer every time the inner `Future` is polled.
|
||||
pub fn new(fut: Fut) -> Self {
|
||||
let owner = Owner::current();
|
||||
let observer = Observer::get();
|
||||
|
@ -54,6 +58,7 @@ impl<Fut: Future> Future for ScopedFuture<Fut> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Utilities used to track whether asynchronous computeds are currently loading.
|
||||
pub mod suspense {
|
||||
use crate::{
|
||||
signal::ArcRwSignal,
|
||||
|
@ -64,10 +69,13 @@ pub mod suspense {
|
|||
use slotmap::{DefaultKey, SlotMap};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Sends a one-time notification that the resource being read from is "local only," i.e.,
|
||||
/// that it will only run on the client, not the server.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LocalResourceNotifier(Arc<Mutex<Option<Sender<()>>>>);
|
||||
|
||||
impl LocalResourceNotifier {
|
||||
/// Send the notification. If the inner channel has already been used, this does nothing.
|
||||
pub fn notify(&mut self) {
|
||||
if let Some(tx) = self.0.lock().or_poisoned().take() {
|
||||
tx.send(()).unwrap();
|
||||
|
@ -81,12 +89,15 @@ pub mod suspense {
|
|||
}
|
||||
}
|
||||
|
||||
/// Tracks the collection of active async tasks.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SuspenseContext {
|
||||
/// The set of active tasks.
|
||||
pub tasks: ArcRwSignal<SlotMap<DefaultKey, ()>>,
|
||||
}
|
||||
|
||||
impl SuspenseContext {
|
||||
/// Generates a unique task ID.
|
||||
pub fn task_id(&self) -> TaskHandle {
|
||||
let key = self.tasks.write().insert(());
|
||||
TaskHandle {
|
||||
|
|
Loading…
Reference in a new issue