mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-23 04:33:06 +00:00
Merge pull request #1128 from marc2332/feat/improved-docs
feat(docs): Improved shared state, use_effect and use_memo docs
This commit is contained in:
commit
78d383bf1f
7 changed files with 180 additions and 19 deletions
|
@ -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)
|
||||
|
|
41
docs/guide/src/en/async/use_effect.md
Normal file
41
docs/guide/src/en/async/use_effect.md
Normal file
|
@ -0,0 +1,41 @@
|
|||
# UseEffect
|
||||
|
||||
[`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
|
||||
|
||||
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
|
||||
|
||||
```rust, no_run
|
||||
#[inline_props]
|
||||
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,)| {
|
||||
to_owned![name];
|
||||
async move {
|
||||
let user = fetch_user(id).await;
|
||||
name.set(user.name);
|
||||
}
|
||||
});
|
||||
|
||||
// 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!(
|
||||
p { "{name}" }
|
||||
)
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
render!(Profile { id: 0 })
|
||||
}
|
||||
```
|
19
docs/guide/src/en/interactivity/memoization.md
Normal file
19
docs/guide/src/en/interactivity/memoization.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Memoization
|
||||
|
||||
[`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]
|
||||
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 })
|
||||
}
|
||||
```
|
|
@ -32,7 +32,7 @@ Finally, a third component will render the other two as children. It will be res
|
|||
|
||||
![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.
|
||||
|
||||
|
@ -42,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:
|
||||
|
||||
|
@ -62,7 +62,7 @@ As a result, any child component of `App` (direct or not), can access the `DarkM
|
|||
{{#include ../../../examples/meme_editor_dark_mode.rs:use_context}}
|
||||
```
|
||||
|
||||
> `use_context` returns `Option<UseSharedState<DarkMode>>` here. If the context has been provided, the value is `Some(UseSharedState<DarkMode>)`, which you can call `.read` or `.write` on, similarly to `UseRef`. Otherwise, the value is `None`.
|
||||
> `use_shared_state` returns `Option<UseSharedState<DarkMode>>` here. If the context has been provided, the value is `Some(UseSharedState<DarkMode>)`, 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):
|
||||
|
||||
|
|
77
examples/shared_state.rs
Normal file
77
examples/shared_state.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(App);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CoolData {
|
||||
data: HashMap<usize, String>,
|
||||
}
|
||||
|
||||
impl CoolData {
|
||||
pub fn new(data: HashMap<usize, String>) -> 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, || CoolData::new(HashMap::from([
|
||||
(0, "Hello, World!".to_string()),
|
||||
(1, "Dioxus is amazing!".to_string())
|
||||
])));
|
||||
|
||||
render!(
|
||||
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::<CoolData>(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::<CoolData>(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}"
|
||||
})
|
||||
}
|
|
@ -9,17 +9,33 @@ 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: usize) -> Element {
|
||||
/// let name = use_state(cx, || None);
|
||||
///
|
||||
/// // 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: 0 })
|
||||
/// }
|
||||
/// ```
|
||||
pub fn use_effect<T, F, D>(cx: &ScopeState, dependencies: D, future: impl FnOnce(D::Out) -> F)
|
||||
|
|
|
@ -2,21 +2,27 @@ 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<T, D>(cx: &ScopeState, dependencies: D, callback: impl FnOnce(D::Out) -> T) -> &T
|
||||
|
|
Loading…
Reference in a new issue