From df0d45be486fe9f966bb8fad8ec71c7648bee92e Mon Sep 17 00:00:00 2001 From: andrey Date: Tue, 27 Feb 2024 10:15:38 +0800 Subject: [PATCH] added better documentation and code snipppets for the use_resource, use_future, use_effect and use_context hooks --- packages/hooks/src/use_context.rs | 27 +++++++++++++++++++++++++++ packages/hooks/src/use_coroutine.rs | 3 ++- packages/hooks/src/use_effect.rs | 18 +++++++++++++++--- packages/hooks/src/use_future.rs | 24 ++++++++++++++++++++++++ packages/hooks/src/use_resource.rs | 26 +++++++++++++++++++++++++- packages/signals/src/impls.rs | 1 + packages/signals/src/signal.rs | 4 ++-- packages/signals/tests/create.rs | 3 +++ 8 files changed, 99 insertions(+), 7 deletions(-) diff --git a/packages/hooks/src/use_context.rs b/packages/hooks/src/use_context.rs index ac3bb62ea..5deafd417 100644 --- a/packages/hooks/src/use_context.rs +++ b/packages/hooks/src/use_context.rs @@ -14,6 +14,18 @@ pub fn try_use_context() -> Option { /// Consume some context in the tree, providing a sharable handle to the value /// /// Does not regenerate the value if the value is changed at the parent. +/// ```rust +/// fn Parent() -> Element { +/// use_context_provider(|| Theme::Dark); +/// rsx! { Child {} } +/// } +/// #[component] +/// fn Child() -> Element { +/// //gets context provided by parent element with use_context_provider +/// let user_theme = use_context::(); +/// rsx! { "user using dark mode: {user_theme == Theme::Dark}" } +/// } +/// ``` #[must_use] pub fn use_context() -> T { use_hook(|| consume_context::()) @@ -22,6 +34,21 @@ pub fn use_context() -> T { /// Provide some context via the tree and return a reference to it /// /// Once the context has been provided, it is immutable. Mutations should be done via interior mutability. +/// Context can be read by any child components of the context provider, and is a solution to prop +/// drilling, using a context provider with a Signal inside is a good way to provide global/shared +/// state in your app: +/// ```rust +///fn app() -> Element { +/// use_context_provider(|| Signal::new(0)); +/// rsx! { Child {} } +///} +// This component does read from the signal, so when the signal changes it will rerun +///#[component] +///fn Child() -> Element { +/// let signal: Signal = use_context(); +/// rsx! { "{signal}" } +///} +/// ``` pub fn use_context_provider(f: impl FnOnce() -> T) -> T { use_hook(|| { let val = f(); diff --git a/packages/hooks/src/use_coroutine.rs b/packages/hooks/src/use_coroutine.rs index 06f342ceb..4834c5431 100644 --- a/packages/hooks/src/use_coroutine.rs +++ b/packages/hooks/src/use_coroutine.rs @@ -93,7 +93,8 @@ where } /// Get a handle to a coroutine higher in the tree -/// +/// Analagous to use_context_provider and use_context, +/// but used for coroutines specifically /// See the docs for [`use_coroutine`] for more details. #[must_use] pub fn use_coroutine_handle() -> Coroutine { diff --git a/packages/hooks/src/use_effect.rs b/packages/hooks/src/use_effect.rs index a3d16f420..97a603b21 100644 --- a/packages/hooks/src/use_effect.rs +++ b/packages/hooks/src/use_effect.rs @@ -1,10 +1,22 @@ use dioxus_core::prelude::*; use dioxus_signals::ReactiveContext; -/// Create a new effect. The effect will be run immediately and whenever any signal it reads changes. -/// The signal will be owned by the current component and will be dropped when the component is dropped. -/// +/// use_effect will subscribe to any changes in the signal values it captures +/// effects will always run after first mount and then whenever the signal values change /// If the use_effect call was skipped due to an early return, the effect will no longer activate. +/// ```rust +/// fn app() -> Element { +/// let mut count = use_signal(|| 0); +/// //the effect runs again each time count changes +/// use_effect(move || println!("Count changed to {count}")); +/// +/// rsx! { +/// h1 { "High-Five counter: {count}" } +/// button { onclick: move |_| count += 1, "Up high!" } +/// button { onclick: move |_| count -= 1, "Down low!" } +/// } +/// } +/// ``` pub fn use_effect(mut callback: impl FnMut() + 'static) { // let mut run_effect = use_hook(|| CopyValue::new(true)); // use_hook_did_run(move |did_run| run_effect.set(did_run)); diff --git a/packages/hooks/src/use_future.rs b/packages/hooks/src/use_future.rs index 4d69879aa..d2619f624 100644 --- a/packages/hooks/src/use_future.rs +++ b/packages/hooks/src/use_future.rs @@ -12,6 +12,30 @@ use std::future::Future; /// /// The future is spawned on the next call to `flush_sync` which means that it will not run on the server. /// To run a future on the server, you should use `spawn` directly. +/// use_future assumes your future will never complete - **it won't return a value**. +/// If you want to return a value, use `use_resource` instead. +/// ```rust +/// fn app() -> Element { +/// let mut count = use_signal(|| 0); +/// let mut running = use_signal(|| true); +/// // use_future will spawn an infinitely running future that can be started and stopped +/// use_future(move || async move { +/// loop { +/// if running() { +/// count += 1; +/// } +/// tokio::time::sleep(Duration::from_millis(400)).await; +/// } +/// }); +/// rsx! { +/// div { +/// h1 { "Current count: {count}" } +/// button { onclick: move |_| running.toggle(), "Start/Stop the count"} +/// button { onclick: move |_| count.set(0), "Reset the count" } +/// } +/// } +/// } +/// ``` pub fn use_future(mut future: impl FnMut() -> F + 'static) -> UseFuture where F: Future + 'static, diff --git a/packages/hooks/src/use_resource.rs b/packages/hooks/src/use_resource.rs index 222b62e7e..389697d74 100644 --- a/packages/hooks/src/use_resource.rs +++ b/packages/hooks/src/use_resource.rs @@ -10,8 +10,32 @@ use futures_util::{future, pin_mut, FutureExt}; use std::future::Future; /// A memo that resolve to a value asynchronously. +/// Unlike `use_future`, `use_resource` runs on the **server** +/// See [`Resource`] for more details. +/// ```rust +///fn app() -> Element { +/// let country = use_signal(|| WeatherLocation { +/// city: "Berlin".to_string(), +/// country: "Germany".to_string(), +/// coordinates: (52.5244, 13.4105) +/// }); /// -/// This runs on the server +/// let current_weather = //run a future inside the use_resource hook +/// use_resource(move || async move { get_weather(&country.read().clone()).await }); +/// +/// rsx! { +/// //the value of the future can be polled to +/// //conditionally render elements based off if the future +/// //finished (Some(Ok(_)), errored Some(Err(_)), +/// //or is still finishing (None) +/// match current_weather.value() { +/// Some(Ok(weather)) => WeatherElement { weather }, +/// Some(Err(e)) => p { "Loading weather failed, {e}" } +/// None => p { "Loading..." } +/// } +/// } +///} +/// ``` #[must_use = "Consider using `cx.spawn` to run a future without reading its value"] pub fn use_resource(future: impl Fn() -> F + 'static) -> Resource where diff --git a/packages/signals/src/impls.rs b/packages/signals/src/impls.rs index 3d120b54e..9cc790a48 100644 --- a/packages/signals/src/impls.rs +++ b/packages/signals/src/impls.rs @@ -5,6 +5,7 @@ use crate::write::Writable; use crate::{GlobalMemo, GlobalSignal, MappedSignal, ReadOnlySignal, SignalData}; use generational_box::{AnyStorage, Storage}; +use std::ops::Index; use std::{ fmt::{Debug, Display}, ops::{Add, Div, Mul, Sub}, diff --git a/packages/signals/src/signal.rs b/packages/signals/src/signal.rs index 44de51082..3d2ea96a2 100644 --- a/packages/signals/src/signal.rs +++ b/packages/signals/src/signal.rs @@ -10,8 +10,8 @@ use generational_box::{AnyStorage, Storage, SyncStorage, UnsyncStorage}; use std::{ any::Any, collections::HashSet, - ops::{Deref, DerefMut}, - sync::Mutex, + ops::{Deref, DerefMut, Index, IndexMut}, + sync::{Arc, Mutex}, }; /// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking. diff --git a/packages/signals/tests/create.rs b/packages/signals/tests/create.rs index 7b17310b6..f9caddd2e 100644 --- a/packages/signals/tests/create.rs +++ b/packages/signals/tests/create.rs @@ -1,5 +1,8 @@ #![allow(unused, non_upper_case_globals, non_snake_case)] +use std::collections::HashMap; +use std::sync::Arc; + use dioxus::prelude::*; use dioxus_core::ElementId; use dioxus_core::NoOpMutations;