diff --git a/examples/future.rs b/examples/future.rs index 130d526b2..76b6c8e7f 100644 --- a/examples/future.rs +++ b/examples/future.rs @@ -1,7 +1,7 @@ //! A simple example that shows how to use the use_future hook to run a background task. //! -//! 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. +//! use_future won't return a value, analagous to use_effect. +//! If you want to return a value from a future, use use_resource instead. use dioxus::prelude::*; use std::time::Duration; diff --git a/packages/hooks/src/use_context.rs b/packages/hooks/src/use_context.rs index ac3bb62ea..9194dbd2f 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,24 @@ 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! { +/// button { onclick: move |_| signal += 1, "increment context" } +/// p {"{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..0028bc7c5 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..53a67087c 100644 --- a/packages/hooks/src/use_future.rs +++ b/packages/hooks/src/use_future.rs @@ -8,10 +8,34 @@ use dioxus_signals::*; use dioxus_signals::{Readable, Writable}; use std::future::Future; -/// A hook that allows you to spawn a future -/// +/// A hook that allows you to spawn a future. +/// This future will **not** run on the server /// 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` **won't return a value**. +/// If you want to return a value from a future, 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/read.rs b/packages/signals/src/read.rs index 63de45047..7effa39f7 100644 --- a/packages/signals/src/read.rs +++ b/packages/signals/src/read.rs @@ -39,7 +39,9 @@ pub trait Readable { MappedSignal::new(try_read, peek) } - /// Get the current value of the state. If this is a signal, this will subscribe the current scope to the signal. If the value has been dropped, this will panic. + /// Get the current value of the state. If this is a signal, this will subscribe the current scope to the signal. + /// If the value has been dropped, this will panic. Calling this on a Signal is the same as + /// using the signal() syntax to read and subscribe to its value #[track_caller] fn read(&self) -> ReadableRef { self.try_read().unwrap()