Merge branch 'master' into master
This commit is contained in:
313 files changed
+23988
-2334
No files matched your search
@@ -2,11 +2,11 @@
|
||||
|
||||
The `use_future` and `use_coroutine` hooks are useful if you want to unconditionally spawn the future. Sometimes, though, you'll want to only spawn a future in response to an event, such as a mouse click. For example, suppose you need to send a request when the user clicks a "log in" button. For this, you can use `cx.spawn`:
|
||||
|
||||
```rust
|
||||
```rust, no_run
|
||||
{{#include ../../../examples/spawn.rs:spawn}}
|
||||
```
|
||||
|
||||
> Note: `spawn` will always spawn a *new* future. You most likely don't want to call it on every render.
|
||||
> Note: `spawn` will always spawn a _new_ future. You most likely don't want to call it on every render.
|
||||
|
||||
Calling `spawn` will give you a `JoinHandle` which lets you cancel or pause the future.
|
||||
|
||||
@@ -14,6 +14,6 @@ Calling `spawn` will give you a `JoinHandle` which lets you cancel or pause the
|
||||
|
||||
Sometimes, you might want to spawn a background task that needs multiple threads or talk to hardware that might block your app code. In these cases, we can directly spawn a Tokio task from our future. For Dioxus-Desktop, your task will be spawned onto Tokio's Multithreaded runtime:
|
||||
|
||||
```rust
|
||||
```rust, no_run
|
||||
{{#include ../../../examples/spawn.rs:tokio}}
|
||||
```
|
||||
@@ -8,7 +8,7 @@ Like regular futures, code in a coroutine will run until the next `await` point
|
||||
|
||||
The `use_coroutine` hook allows you to create a coroutine. Most coroutines we write will be polling loops using async/await.
|
||||
|
||||
```rust
|
||||
```rust, no_run
|
||||
fn app(cx: Scope) -> Element {
|
||||
let ws: &UseCoroutine<()> = use_coroutine(cx, |rx| async move {
|
||||
// Connect to some sort of service
|
||||
@@ -26,7 +26,7 @@ For many services, a simple async loop will handle the majority of use cases.
|
||||
|
||||
However, if we want to temporarily disable the coroutine, we can "pause" it using the `pause` method, and "resume" it using the `resume` method:
|
||||
|
||||
```rust
|
||||
```rust, no_run
|
||||
let sync: &UseCoroutine<()> = use_coroutine(cx, |rx| async move {
|
||||
// code for syncing
|
||||
});
|
||||
@@ -58,7 +58,7 @@ The future must be `'static` – so any values captured by the task cannot carry
|
||||
|
||||
You can use [to_owned](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned) to create a clone of the hook handle which can be moved into the async closure.
|
||||
|
||||
```rust
|
||||
```rust, no_run
|
||||
let sync_status = use_state(cx, || Status::Launching);
|
||||
let sync_task = use_coroutine(cx, |rx: UnboundedReceiver<SyncAction>| {
|
||||
let sync_status = sync_status.to_owned();
|
||||
@@ -73,7 +73,7 @@ let sync_task = use_coroutine(cx, |rx: UnboundedReceiver<SyncAction>| {
|
||||
|
||||
To make this a bit less verbose, Dioxus exports the `to_owned!` macro which will create a binding as shown above, which can be quite helpful when dealing with many values.
|
||||
|
||||
```rust
|
||||
```rust, no_run
|
||||
let sync_status = use_state(cx, || Status::Launching);
|
||||
let load_status = use_state(cx, || Status::Launching);
|
||||
let sync_task = use_coroutine(cx, |rx: UnboundedReceiver<SyncAction>| {
|
||||
@@ -88,10 +88,9 @@ let sync_task = use_coroutine(cx, |rx: UnboundedReceiver<SyncAction>| {
|
||||
|
||||
You might've noticed the `use_coroutine` closure takes an argument called `rx`. What is that? Well, a common pattern in complex apps is to handle a bunch of async code at once. With libraries like Redux Toolkit, managing multiple promises at once can be challenging and a common source of bugs.
|
||||
|
||||
With Coroutines, we can centralize our async logic. The `rx` parameter is an Channel that allows code external to the coroutine to send data *into* the coroutine. Instead of looping on an external service, we can loop on the channel itself, processing messages from within our app without needing to spawn a new future. To send data into the coroutine, we would call "send" on the handle.
|
||||
With Coroutines, we can centralize our async logic. The `rx` parameter is an Channel that allows code external to the coroutine to send data _into_ the coroutine. Instead of looping on an external service, we can loop on the channel itself, processing messages from within our app without needing to spawn a new future. To send data into the coroutine, we would call "send" on the handle.
|
||||
|
||||
|
||||
```rust
|
||||
```rust, no_run
|
||||
use futures_util::stream::StreamExt;
|
||||
|
||||
enum ProfileUpdate {
|
||||
@@ -119,13 +118,11 @@ cx.render(rsx!{
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
> Note: In order to use/run the `rx.next().await` statement you will need to extend the [`Stream`] trait (used by [`UnboundedReceiver`]) by adding 'futures_util' as a dependency to your project and adding the `use futures_util::stream::StreamExt;`.
|
||||
|
||||
|
||||
For sufficiently complex apps, we could build a bunch of different useful "services" that loop on channels to update the app.
|
||||
|
||||
```rust
|
||||
```rust, no_run
|
||||
let profile = use_coroutine(cx, profile_service);
|
||||
let editor = use_coroutine(cx, editor_service);
|
||||
let sync = use_coroutine(cx, sync_service);
|
||||
@@ -143,9 +140,9 @@ async fn editor_service(rx: UnboundedReceiver<EditorCommand>) {
|
||||
}
|
||||
```
|
||||
|
||||
We can combine coroutines with [Fermi](https://docs.rs/fermi/latest/fermi/index.html) to emulate Redux Toolkit's Thunk system with much less headache. This lets us store all of our app's state *within* a task and then simply update the "view" values stored in Atoms. It cannot be understated how powerful this technique is: we get all the perks of native Rust tasks with the optimizations and ergonomics of global state. This means your *actual* state does not need to be tied up in a system like Fermi or Redux – the only Atoms that need to exist are those that are used to drive the display/UI.
|
||||
We can combine coroutines with [Fermi](https://docs.rs/fermi/latest/fermi/index.html) to emulate Redux Toolkit's Thunk system with much less headache. This lets us store all of our app's state _within_ a task and then simply update the "view" values stored in Atoms. It cannot be understated how powerful this technique is: we get all the perks of native Rust tasks with the optimizations and ergonomics of global state. This means your _actual_ state does not need to be tied up in a system like Fermi or Redux – the only Atoms that need to exist are those that are used to drive the display/UI.
|
||||
|
||||
```rust
|
||||
```rust, no_run
|
||||
static USERNAME: Atom<String> = Atom(|_| "default".to_string());
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
@@ -169,7 +166,7 @@ fn Banner(cx: Scope) -> Element {
|
||||
|
||||
Now, in our sync service, we can structure our state however we want. We only need to update the view values when ready.
|
||||
|
||||
```rust
|
||||
```rust, no_run
|
||||
use futures_util::stream::StreamExt;
|
||||
|
||||
enum SyncAction {
|
||||
@@ -198,7 +195,7 @@ async fn sync_service(mut rx: UnboundedReceiver<SyncAction>, atoms: AtomRoot) {
|
||||
|
||||
Coroutine handles are automatically injected through the context API. You can use the `use_coroutine_handle` hook with the message type as a generic to fetch a handle.
|
||||
|
||||
```rust
|
||||
```rust, no_run
|
||||
fn Child(cx: Scope) -> Element {
|
||||
let sync_task = use_coroutine_handle::<SyncAction>(cx);
|
||||
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
```
|
||||
@@ -4,21 +4,20 @@
|
||||
|
||||
For example, we can make an API request (using [reqwest](https://docs.rs/reqwest/latest/reqwest/index.html)) inside `use_future`:
|
||||
|
||||
```rust
|
||||
```rust, no_run
|
||||
{{#include ../../../examples/use_future.rs:use_future}}
|
||||
```
|
||||
|
||||
The code inside `use_future` will be submitted to the Dioxus scheduler once the component has rendered.
|
||||
|
||||
We can use `.value()` to get the result of the future. On the first run, since there's no data ready when the component loads, its value will be `None`. However, once the future is finished, the component will be re-rendered and the value will now be `Some(...)`, containing the return value of the closure.
|
||||
We can use `.value()` to get the result of the future. On the first run, since there's no data ready when the component loads, its value will be `None`. However, once the future is finished, the component will be re-rendered and the value will now be `Some(...)`, containing the return value of the closure.
|
||||
|
||||
We can then render that result:
|
||||
|
||||
```rust
|
||||
```rust, no_run
|
||||
{{#include ../../../examples/use_future.rs:render}}
|
||||
```
|
||||
|
||||
|
||||
## Restarting the Future
|
||||
|
||||
The `UseFuture` handle provides a `restart` method. It can be used to execute the future again, producing a new value.
|
||||
@@ -27,7 +26,6 @@ The `UseFuture` handle provides a `restart` method. It can be used to execute th
|
||||
|
||||
Often, you will need to run the future again every time some value (e.g. a prop) changes. Rather than calling `restart` manually, 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
|
||||
```rust, no_run
|
||||
{{#include ../../../examples/use_future.rs:dependency}}
|
||||
```
|
||||
Reference in new issue
Block a user