From 8a2f9f3fcb37e63b64a92a6ce01511b5137dd122 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Wed, 28 Jun 2023 00:15:00 +0200 Subject: [PATCH 01/10] feat(docs): Improved shared state, use_effect and use_memo docs --- docs/guide/src/en/SUMMARY.md | 2 + docs/guide/src/en/async/use_effect.md | 32 +++++++++++ .../guide/src/en/interactivity/memoization.md | 23 ++++++++ .../src/en/interactivity/sharing_state.md | 13 +++-- examples/shared_state.rs | 54 +++++++++++++++++++ packages/hooks/src/useeffect.rs | 25 ++++++--- packages/hooks/src/usememo.rs | 28 ++++++---- 7 files changed, 158 insertions(+), 19 deletions(-) create mode 100644 docs/guide/src/en/async/use_effect.md create mode 100644 docs/guide/src/en/interactivity/memoization.md create mode 100644 examples/shared_state.rs diff --git a/docs/guide/src/en/SUMMARY.md b/docs/guide/src/en/SUMMARY.md index 62ef5adf7..a44439200 100644 --- a/docs/guide/src/en/SUMMARY.md +++ b/docs/guide/src/en/SUMMARY.md @@ -21,10 +21,12 @@ - [Hooks & Component State](interactivity/hooks.md) - [User Input](interactivity/user_input.md) - [Sharing State](interactivity/sharing_state.md) + - [Memoization](interactivity/memoization.md) - [Custom Hooks](interactivity/custom_hooks.md) - [Dynamic Rendering](interactivity/dynamic_rendering.md) - [Routing](interactivity/router.md) - [Async](async/index.md) + - [UseEffect](async/use_effect.md) - [UseFuture](async/use_future.md) - [UseCoroutine](async/use_coroutine.md) - [Spawning Futures](async/spawn.md) diff --git a/docs/guide/src/en/async/use_effect.md b/docs/guide/src/en/async/use_effect.md new file mode 100644 index 000000000..711492500 --- /dev/null +++ b/docs/guide/src/en/async/use_effect.md @@ -0,0 +1,32 @@ +# UseEffect + +[`use_effect`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_effect.html) provides a future that executes after the hooks have been applied. + +Whenever the hooks dependencies change, the future will be re-evaluated. This is useful to syncrhonize with external events. + +If a future is pending when the dependencies change, the previous future will be allowed to continue + +> The `dependencies` is tuple of references to values that are `PartialEq + Clone`. + +```rust, no_run +#[inline_props] +fn Profile(cx: Scope, id: &str) -> Element { + let name = use_state(cx, || "Default name"); + + // Only fetch the user data when the id changes. + use_effect(cx, (id,), |(id,)| async move { + let user = fetch_user(id).await; + name.set(user.name); + }); + + render!( + p { "{name}" } + ) +} + +fn app(cx: Scope) -> Element { + render!( + Profile { id: "dioxusLabs" } + ) +} +``` diff --git a/docs/guide/src/en/interactivity/memoization.md b/docs/guide/src/en/interactivity/memoization.md new file mode 100644 index 000000000..cc67c02c4 --- /dev/null +++ b/docs/guide/src/en/interactivity/memoization.md @@ -0,0 +1,23 @@ +# Memoization + +[`use_memo`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_memo.html) let's you memorize values and therefore save computation time. This is useful for expensive calculations. + +```rust, no_run +#[inline_props] +fn Calculator(cx: Scope, number: usize) -> Element { + let bigger_number = use_memo(cx, (number,), |(number,)| { + // This will only be calculated when `number` has changed. + number * 100 + }): + + render!( + p { "{bigger_number}" } + ) +} + +fn app(cx: Scope) -> Element { + render!( + Calculator { number: 0 } + ) +} +``` diff --git a/docs/guide/src/en/interactivity/sharing_state.md b/docs/guide/src/en/interactivity/sharing_state.md index 75ace8973..3f9b3cdc2 100644 --- a/docs/guide/src/en/interactivity/sharing_state.md +++ b/docs/guide/src/en/interactivity/sharing_state.md @@ -11,6 +11,7 @@ Suppose we want to build a meme editor. We want to have an input to edit the mem > Of course, in this simple example, we could write everything in one component – but it is better to split everything out in smaller components to make the code more reusable, maintainable, and performant (this is even more important for larger, complex apps). We start with a `Meme` component, responsible for rendering a meme with a given caption: + ```rust {{#include ../../../examples/meme_editor.rs:meme_component}} ``` @@ -24,12 +25,14 @@ We also create a caption editor, completely decoupled from the meme. The caption ``` Finally, a third component will render the other two as children. It will be responsible for keeping the state and passing down the relevant props. + ```rust {{#include ../../../examples/meme_editor.rs:meme_editor}} ``` + ![Meme Editor Screenshot: An old plastic skeleton sitting on a park bench. Caption: "me waiting for a language feature"](./images/meme_editor_screenshot.png) -## Using Context +## Using Shared State Sometimes, some state needs to be shared between multiple components far down the tree, and passing it down through props is very inconvenient. @@ -39,7 +42,7 @@ Suppose now that we want to implement a dark mode toggle for our app. To achieve Now, we could write another `use_state` in the top component, and pass `is_dark_mode` down to every component through props. But think about what will happen as the app grows in complexity – almost every component that renders any CSS is going to need to know if dark mode is enabled or not – so they'll all need the same dark mode prop. And every parent component will need to pass it down to them. Imagine how messy and verbose that would get, especially if we had components several levels deep! -Dioxus offers a better solution than this "prop drilling" – providing context. The [`use_context_provider`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_context_provider.html) hook is similar to `use_ref`, but it makes it available through [`use_context`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_context.html) for all children components. +Dioxus offers a better solution than this "prop drilling" – providing context. The [`use_shared_state_provider`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_shared_state_provider.html) hook is similar to `use_ref`, but it makes it available through [`use_shared_state`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_shared_state.html) for all children components. First, we have to create a struct for our dark mode configuration: @@ -48,19 +51,21 @@ First, we have to create a struct for our dark mode configuration: ``` Now, in a top-level component (like `App`), we can provide the `DarkMode` context to all children components: + ```rust {{#include ../../../examples/meme_editor_dark_mode.rs:context_provider}} ``` As a result, any child component of `App` (direct or not), can access the `DarkMode` context. + ```rust {{#include ../../../examples/meme_editor_dark_mode.rs:use_context}} ``` -> `use_context` returns `Option>` here. If the context has been provided, the value is `Some(UseSharedState)`, which you can call `.read` or `.write` on, similarly to `UseRef`. Otherwise, the value is `None`. +> `use_shared_state` returns `Option>` here. If the context has been provided, the value is `Some(UseSharedState)`, which you can call `.read` or `.write` on, similarly to `UseRef`. Otherwise, the value is `None`. For example, here's how we would implement the dark mode toggle, which both reads the context (to determine what color it should render) and writes to it (to toggle dark mode): + ```rust {{#include ../../../examples/meme_editor_dark_mode.rs:toggle}} ``` - diff --git a/examples/shared_state.rs b/examples/shared_state.rs new file mode 100644 index 000000000..1072ce50d --- /dev/null +++ b/examples/shared_state.rs @@ -0,0 +1,54 @@ +#![allow(non_snake_case)] + +use dioxus::prelude::*; + +fn main() { + dioxus_desktop::launch(App); +} + +struct DarkMode(bool); + +#[rustfmt::skip] +pub fn App(cx: Scope) -> Element { + use_shared_state_provider(cx, || DarkMode(false)); + + render!( + DarkModeToggle {}, + AppBody {} + ) +} + +pub fn DarkModeToggle(cx: Scope) -> Element { + let dark_mode = use_shared_state::(cx).unwrap(); + + let style = if dark_mode.read().0 { + "color:white" + } else { + "" + }; + + cx.render(rsx!(label { + style: "{style}", + "Dark Mode", + input { + r#type: "checkbox", + oninput: move |event| { + let is_enabled = event.value == "true"; + dark_mode.write().0 = is_enabled; + }, + }, + })) +} + +fn AppBody(cx: Scope) -> Element { + let dark_mode = use_shared_state::(cx).unwrap(); + + let is_dark_mode = dark_mode.read().0; + let answer = if is_dark_mode { "Yes" } else { "No" }; + + render!( + p { + "Is Dark mode enabled? {answer}" + } + ) +} diff --git a/packages/hooks/src/useeffect.rs b/packages/hooks/src/useeffect.rs index f9ef70b84..b52b89c3c 100644 --- a/packages/hooks/src/useeffect.rs +++ b/packages/hooks/src/useeffect.rs @@ -9,17 +9,30 @@ use crate::UseFutureDep; /// If a future is pending when the dependencies change, the previous future /// will be allowed to continue /// -/// - dependencies: a tuple of references to values that are PartialEq + Clone +/// - dependencies: a tuple of references to values that are `PartialEq` + `Clone` /// /// ## Examples /// -/// ```rust, ignore +/// ```rust, no_run /// /// #[inline_props] -/// fn app(cx: Scope, name: &str) -> Element { -/// use_effect(cx, (name,), |(name,)| async move { -/// set_title(name); -/// })) +/// fn Profile(cx: Scope, id: &str) -> Element { +/// let name = use_state(cx, || "Default name"); +/// +/// use_effect(cx, (id,), |(id,)| async move { +/// let user = fetch_user(id).await; +/// name.set(user.name); +/// }); +/// +/// render!( +/// p { "{name}" } +/// ) +/// } +/// +/// fn app(cx: Scope) -> Element { +/// render!( +/// Profile { id: "dioxusLabs" } +/// ) /// } /// ``` pub fn use_effect(cx: &ScopeState, dependencies: D, future: impl FnOnce(D::Out) -> F) diff --git a/packages/hooks/src/usememo.rs b/packages/hooks/src/usememo.rs index 8cc5540e6..f99d1e893 100644 --- a/packages/hooks/src/usememo.rs +++ b/packages/hooks/src/usememo.rs @@ -2,21 +2,31 @@ use dioxus_core::ScopeState; use crate::UseFutureDep; -/// A hook that provides a callback that executes after the hooks have been applied +/// A hook that provides a callback that executes if the dependencies change. +/// This is useful to avoid running computation-expensive calculations even when the data doesn't change. /// -/// Whenever the hooks dependencies change, the callback will be re-evaluated. -/// -/// - dependencies: a tuple of references to values that are PartialEq + Clone +/// - dependencies: a tuple of references to values that are `PartialEq` + `Clone` /// /// ## Examples /// -/// ```rust, ignore +/// ```rust, no_run /// /// #[inline_props] -/// fn app(cx: Scope, name: &str) -> Element { -/// use_memo(cx, (name,), |(name,)| { -/// expensive_computation(name); -/// })) +/// fn Calculator(cx: Scope, number: usize) -> Element { +/// let bigger_number = use_memo(cx, (number,), |(number,)| { +/// // This will only be calculated when `number` has changed. +/// number * 100 +/// }): +/// +/// render!( +/// p { "{bigger_number}" } +/// ) +/// } +/// +/// fn app(cx: Scope) -> Element { +/// render!( +/// Calculator { number: 0 } +/// ) /// } /// ``` pub fn use_memo(cx: &ScopeState, dependencies: D, callback: impl FnOnce(D::Out) -> T) -> &T From a3978d60a898088c68376c51b39c30452c78b854 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Wed, 28 Jun 2023 00:17:36 +0200 Subject: [PATCH 02/10] update --- examples/shared_state.rs | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/examples/shared_state.rs b/examples/shared_state.rs index 1072ce50d..6cd4d874e 100644 --- a/examples/shared_state.rs +++ b/examples/shared_state.rs @@ -21,23 +21,13 @@ pub fn App(cx: Scope) -> Element { pub fn DarkModeToggle(cx: Scope) -> Element { let dark_mode = use_shared_state::(cx).unwrap(); - let style = if dark_mode.read().0 { - "color:white" - } else { - "" - }; - - cx.render(rsx!(label { - style: "{style}", - "Dark Mode", - input { - r#type: "checkbox", - oninput: move |event| { - let is_enabled = event.value == "true"; - dark_mode.write().0 = is_enabled; - }, + render!(input { + r#type: "checkbox", + oninput: move |event| { + let is_enabled = event.value == "true"; + dark_mode.write().0 = is_enabled; }, - })) + }) } fn AppBody(cx: Scope) -> Element { From bf0247a7dc84aeafd2f91656f1e7bcc5b19298de Mon Sep 17 00:00:00 2001 From: marc2332 Date: Wed, 28 Jun 2023 20:01:28 +0200 Subject: [PATCH 03/10] tweaks --- docs/guide/src/en/async/use_effect.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/guide/src/en/async/use_effect.md b/docs/guide/src/en/async/use_effect.md index 711492500..8b82828db 100644 --- a/docs/guide/src/en/async/use_effect.md +++ b/docs/guide/src/en/async/use_effect.md @@ -2,11 +2,14 @@ [`use_effect`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_effect.html) provides a future that executes after the hooks have been applied. -Whenever the hooks dependencies change, the future will be re-evaluated. This is useful to syncrhonize with external events. +Whenever the hooks [dependencies](#dependencies) change, the future will be re-evaluated. This is useful to syncrhonize with external events. -If a future is pending when the dependencies change, the previous future will be allowed to continue -> The `dependencies` is tuple of references to values that are `PartialEq + Clone`. +## Dependencies + +You might want to call `use_effect` only when some value changes. For example, you might want to fetch a user's data only when the user id changes. You can provide a tuple of "dependencies" to the hook. It will automatically re-run the future when any of those dependencies change. + +Example: ```rust, no_run #[inline_props] @@ -29,4 +32,4 @@ fn app(cx: Scope) -> Element { Profile { id: "dioxusLabs" } ) } -``` +``` \ No newline at end of file From d00b10e83ec9b75eb776b6093686e661d50d23b3 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Thu, 29 Jun 2023 14:14:29 +0200 Subject: [PATCH 04/10] tweaks --- docs/guide/src/en/async/use_effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/src/en/async/use_effect.md b/docs/guide/src/en/async/use_effect.md index 8b82828db..4980e2d20 100644 --- a/docs/guide/src/en/async/use_effect.md +++ b/docs/guide/src/en/async/use_effect.md @@ -7,7 +7,7 @@ Whenever the hooks [dependencies](#dependencies) change, the future will be re-e ## Dependencies -You might want to call `use_effect` only when some value changes. For example, you might want to fetch a user's data only when the user id changes. You can provide a tuple of "dependencies" to the hook. It will automatically re-run the future when any of those dependencies change. +You can make the future re-run when some value changes. For example, you might want to fetch a user's data only when the user id changes. You can provide a tuple of "dependencies" to the hook. It will automatically re-run the future when any of those dependencies change. Example: From fb2669c3be5800ff9ab9a8ca680283021bdda445 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Sun, 2 Jul 2023 22:41:00 +0200 Subject: [PATCH 05/10] improved shared state example --- examples/shared_state.rs | 89 +++++++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 28 deletions(-) diff --git a/examples/shared_state.rs b/examples/shared_state.rs index 6cd4d874e..2d5f6f125 100644 --- a/examples/shared_state.rs +++ b/examples/shared_state.rs @@ -1,44 +1,77 @@ #![allow(non_snake_case)] +use std::collections::HashMap; + use dioxus::prelude::*; fn main() { dioxus_desktop::launch(App); } -struct DarkMode(bool); +#[derive(Default)] +struct CoolData { + data: HashMap, +} + +impl CoolData { + pub fn new(data: HashMap) -> Self { + Self { data } + } + + pub fn view(&self, id: &usize) -> Option<&String> { + self.data.get(id) + } + + pub fn set(&mut self, id: usize, data: String) { + self.data.insert(id, data); + } +} #[rustfmt::skip] pub fn App(cx: Scope) -> Element { - use_shared_state_provider(cx, || DarkMode(false)); + use_shared_state_provider(cx, || CoolData::new(HashMap::from([ + (0, "Hello, World!".to_string()), + (1, "Dioxus is amazing!".to_string()) + ]))); render!( - DarkModeToggle {}, - AppBody {} - ) -} - -pub fn DarkModeToggle(cx: Scope) -> Element { - let dark_mode = use_shared_state::(cx).unwrap(); - - render!(input { - r#type: "checkbox", - oninput: move |event| { - let is_enabled = event.value == "true"; - dark_mode.write().0 = is_enabled; - }, - }) -} - -fn AppBody(cx: Scope) -> Element { - let dark_mode = use_shared_state::(cx).unwrap(); - - let is_dark_mode = dark_mode.read().0; - let answer = if is_dark_mode { "Yes" } else { "No" }; - - render!( - p { - "Is Dark mode enabled? {answer}" + DataEditor { + id: 0 + } + DataEditor { + id: 1 + } + DataView { + id: 0 + } + DataView { + id: 1 } ) } + +#[inline_props] +fn DataEditor(cx: Scope, id: usize) -> Element { + let cool_data = use_shared_state::(cx).unwrap().read(); + + let my_data = &cool_data.view(&id).unwrap(); + + render!(p { + "{my_data}" + }) +} + +#[inline_props] +fn DataView(cx: Scope, id: usize) -> Element { + let cool_data = use_shared_state::(cx).unwrap(); + + let oninput = |e: FormEvent| cool_data.write().set(*id, e.value.clone()); + + let cool_data = cool_data.read(); + let my_data = &cool_data.view(&id).unwrap(); + + render!(input { + oninput: oninput, + value: "{my_data}" + }) +} From 052ae145bfb9fda38f72b43341d6059255282a61 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Sun, 2 Jul 2023 23:00:26 +0200 Subject: [PATCH 06/10] updated docs --- docs/guide/src/en/async/use_effect.md | 22 ++++++++++--------- .../guide/src/en/interactivity/memoization.md | 8 ++----- packages/hooks/src/useeffect.rs | 21 ++++++++++-------- packages/hooks/src/usememo.rs | 8 ++----- 4 files changed, 28 insertions(+), 31 deletions(-) diff --git a/docs/guide/src/en/async/use_effect.md b/docs/guide/src/en/async/use_effect.md index 4980e2d20..bd73453a9 100644 --- a/docs/guide/src/en/async/use_effect.md +++ b/docs/guide/src/en/async/use_effect.md @@ -4,7 +4,6 @@ Whenever the hooks [dependencies](#dependencies) change, the future will be re-evaluated. This is useful to syncrhonize with external events. - ## Dependencies You can make the future re-run when some value changes. For example, you might want to fetch a user's data only when the user id changes. You can provide a tuple of "dependencies" to the hook. It will automatically re-run the future when any of those dependencies change. @@ -13,23 +12,26 @@ Example: ```rust, no_run #[inline_props] -fn Profile(cx: Scope, id: &str) -> Element { - let name = use_state(cx, || "Default name"); +fn Profile(cx: Scope, id: usize) -> Element { + let name = use_state(cx, || None); // Only fetch the user data when the id changes. - use_effect(cx, (id,), |(id,)| async move { - let user = fetch_user(id).await; - name.set(user.name); + use_effect(cx, (id,), |(id,)| { + to_owned![name]; + async move { + let user = fetch_user(id).await; + name.set(user.name); + } }); + let name = name.get().clone().unwrap_or("Loading...".to_string()); + render!( p { "{name}" } ) } fn app(cx: Scope) -> Element { - render!( - Profile { id: "dioxusLabs" } - ) + render!(Profile { id: 0 }) } -``` \ No newline at end of file +``` diff --git a/docs/guide/src/en/interactivity/memoization.md b/docs/guide/src/en/interactivity/memoization.md index cc67c02c4..72a709bcc 100644 --- a/docs/guide/src/en/interactivity/memoization.md +++ b/docs/guide/src/en/interactivity/memoization.md @@ -8,16 +8,12 @@ fn Calculator(cx: Scope, number: usize) -> Element { let bigger_number = use_memo(cx, (number,), |(number,)| { // This will only be calculated when `number` has changed. number * 100 - }): - + }); render!( p { "{bigger_number}" } ) } - fn app(cx: Scope) -> Element { - render!( - Calculator { number: 0 } - ) + render!(Calculator { number: 0 }) } ``` diff --git a/packages/hooks/src/useeffect.rs b/packages/hooks/src/useeffect.rs index b52b89c3c..aec1f9c9a 100644 --- a/packages/hooks/src/useeffect.rs +++ b/packages/hooks/src/useeffect.rs @@ -14,25 +14,28 @@ use crate::UseFutureDep; /// ## Examples /// /// ```rust, no_run -/// /// #[inline_props] -/// fn Profile(cx: Scope, id: &str) -> Element { -/// let name = use_state(cx, || "Default name"); +/// fn Profile(cx: Scope, id: usize) -> Element { +/// let name = use_state(cx, || None); /// -/// use_effect(cx, (id,), |(id,)| async move { -/// let user = fetch_user(id).await; -/// name.set(user.name); +/// // Only fetch the user data when the id changes. +/// use_effect(cx, (id,), |(id,)| { +/// to_owned![name]; +/// async move { +/// let user = fetch_user(id).await; +/// name.set(user.name); +/// } /// }); /// +/// let name = name.get().clone().unwrap_or("Loading...".to_string()); +/// /// render!( /// p { "{name}" } /// ) /// } /// /// fn app(cx: Scope) -> Element { -/// render!( -/// Profile { id: "dioxusLabs" } -/// ) +/// render!(Profile { id: 0 }) /// } /// ``` pub fn use_effect(cx: &ScopeState, dependencies: D, future: impl FnOnce(D::Out) -> F) diff --git a/packages/hooks/src/usememo.rs b/packages/hooks/src/usememo.rs index f99d1e893..d3d991ccc 100644 --- a/packages/hooks/src/usememo.rs +++ b/packages/hooks/src/usememo.rs @@ -16,17 +16,13 @@ use crate::UseFutureDep; /// let bigger_number = use_memo(cx, (number,), |(number,)| { /// // This will only be calculated when `number` has changed. /// number * 100 -/// }): -/// +/// }); /// render!( /// p { "{bigger_number}" } /// ) /// } -/// /// fn app(cx: Scope) -> Element { -/// render!( -/// Calculator { number: 0 } -/// ) +/// render!(Calculator { number: 0 }) /// } /// ``` pub fn use_memo(cx: &ScopeState, dependencies: D, callback: impl FnOnce(D::Out) -> T) -> &T From 1f3f72edc30e98fc0e61d1f940e49828beb1dc76 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Thu, 6 Jul 2023 17:23:37 +0200 Subject: [PATCH 07/10] updated --- docs/guide/src/en/async/use_effect.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/guide/src/en/async/use_effect.md b/docs/guide/src/en/async/use_effect.md index bd73453a9..f509921fc 100644 --- a/docs/guide/src/en/async/use_effect.md +++ b/docs/guide/src/en/async/use_effect.md @@ -1,14 +1,12 @@ # UseEffect -[`use_effect`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_effect.html) provides a future that executes after the hooks have been applied. - -Whenever the hooks [dependencies](#dependencies) change, the future will be re-evaluated. This is useful to syncrhonize with external events. +[`use_effect`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_effect.html) let's you run a callback that returns a future, only it's [dependencies](#dependencies) change. This is useful to syncrhonize with external events. ## Dependencies -You can make the future re-run when some value changes. For example, you might want to fetch a user's data only when the user id changes. You can provide a tuple of "dependencies" to the hook. It will automatically re-run the future when any of those dependencies change. +You can make the callback re-run when some value changes. For example, you might want to fetch a user's data only when the user id changes. You can provide a tuple of "dependencies" to the hook. It will automatically re-run it when any of those dependencies change. -Example: +## Example ```rust, no_run #[inline_props] @@ -24,6 +22,12 @@ fn Profile(cx: Scope, id: usize) -> Element { } }); + // Because the dependencies are empty, this will only run once. + // An empty tuple is always equal to an empty tuple. + use_effect(cx, (), |()| async move { + println!("Hello, World!"); + }); + let name = name.get().clone().unwrap_or("Loading...".to_string()); render!( From 39bce476aedc99f431114e667aa430bd0938d605 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Thu, 13 Jul 2023 19:06:43 +0200 Subject: [PATCH 08/10] tweaks --- docs/guide/src/en/async/use_effect.md | 2 +- examples/test.rs | 63 +++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 examples/test.rs diff --git a/docs/guide/src/en/async/use_effect.md b/docs/guide/src/en/async/use_effect.md index f509921fc..9dc5a8739 100644 --- a/docs/guide/src/en/async/use_effect.md +++ b/docs/guide/src/en/async/use_effect.md @@ -1,6 +1,6 @@ # UseEffect -[`use_effect`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_effect.html) let's you run a callback that returns a future, only it's [dependencies](#dependencies) change. This is useful to syncrhonize with external events. +[`use_effect`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_effect.html) lets you run a callback that returns a future, which will be re-run when it's [dependencies](#dependencies) change. This is useful to syncrhonize with external events. ## Dependencies diff --git a/examples/test.rs b/examples/test.rs new file mode 100644 index 000000000..546934481 --- /dev/null +++ b/examples/test.rs @@ -0,0 +1,63 @@ +use dioxus::prelude::*; +use dioxus_router::*; + +fn main() { + // init debug tool for WebAssembly + wasm_logger::init(wasm_logger::Config::default()); + console_error_panic_hook::set_once(); + + dioxus_web::launch(app); +} + +fn Works1(cx: Scope) -> Element { + render!( + p { + "this is 1" + } + a { + href: "#section", + "section" + } + Link { + to: "/2", + p { + "go to 2" + } + } + p { + "{\"AAAA\n\".repeat(999)}" + } + h2 { + id: "section", + "section" + } + ) +} + +fn Works2(cx: Scope) -> Element { + render!( + p { + "this is 2" + Link { + to: "/", + p { + "go to 1" + } + } + ) +} + +fn app(cx: Scope) -> Element { + cx.render(rsx! ( + Router { + Route { + to: "/", + Works1 {} + } + Route { + to: "/2", + Works2 {} + } + } + )) +} \ No newline at end of file From f7dd4190438c3f25fc00502037a1d21d62368002 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Thu, 13 Jul 2023 22:37:05 +0200 Subject: [PATCH 09/10] update --- docs/guide/src/en/interactivity/memoization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/src/en/interactivity/memoization.md b/docs/guide/src/en/interactivity/memoization.md index 72a709bcc..32ec37ce5 100644 --- a/docs/guide/src/en/interactivity/memoization.md +++ b/docs/guide/src/en/interactivity/memoization.md @@ -1,6 +1,6 @@ # Memoization -[`use_memo`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_memo.html) let's you memorize values and therefore save computation time. This is useful for expensive calculations. +[`use_memo`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_memo.html) let's you memorize values and thus save computation time. This is useful for expensive calculations. ```rust, no_run #[inline_props] From c07006e9a21d2dd60bd0f0e44fbd7090a2a9ce0c Mon Sep 17 00:00:00 2001 From: marc2332 Date: Thu, 13 Jul 2023 22:43:46 +0200 Subject: [PATCH 10/10] :bowtie: --- examples/test.rs | 63 ------------------------------------------------ 1 file changed, 63 deletions(-) delete mode 100644 examples/test.rs diff --git a/examples/test.rs b/examples/test.rs deleted file mode 100644 index 546934481..000000000 --- a/examples/test.rs +++ /dev/null @@ -1,63 +0,0 @@ -use dioxus::prelude::*; -use dioxus_router::*; - -fn main() { - // init debug tool for WebAssembly - wasm_logger::init(wasm_logger::Config::default()); - console_error_panic_hook::set_once(); - - dioxus_web::launch(app); -} - -fn Works1(cx: Scope) -> Element { - render!( - p { - "this is 1" - } - a { - href: "#section", - "section" - } - Link { - to: "/2", - p { - "go to 2" - } - } - p { - "{\"AAAA\n\".repeat(999)}" - } - h2 { - id: "section", - "section" - } - ) -} - -fn Works2(cx: Scope) -> Element { - render!( - p { - "this is 2" - Link { - to: "/", - p { - "go to 1" - } - } - ) -} - -fn app(cx: Scope) -> Element { - cx.render(rsx! ( - Router { - Route { - to: "/", - Works1 {} - } - Route { - to: "/2", - Works2 {} - } - } - )) -} \ No newline at end of file