Internationalization: move guide to en folder and preserve its history. (#494)

* chore: move guide to en folder

* docs: add english as language
This commit is contained in:
Jon Kelley authored and GitHub committed 2022-07-09 12:31:46 -04:00
1 parent 7fdc72726d
commit c97ca7dff6
69 files changed
+4 -1

No files matched your search

+9
View File
@@ -0,0 +1,9 @@
# Working with Async
Often, apps need to interact with file systems, network interfaces, hardware, or timers. This chapter provides an overview of using async code in Dioxus.
## The Runtime
By default, Dioxus-Desktop ships with the `Tokio` runtime and automatically sets everything up for you. This is currently not configurable, though it would be easy to write an integration for Dioxus desktop that uses a different asynchronous runtime.
Dioxus is not currently thread-safe, so any async code you write does *not* need to be `Send/Sync`. That means that you can use non-thread-safe structures like `Cell`, `Rc`, and `RefCell`.
+29
View File
@@ -0,0 +1,29 @@
# Spawning Futures
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
{{#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.
The future must be `'static` – so any values captured by the task cannot carry any references to `cx`, such as a `UseState`.
However, since you'll typically need a way to update the value of a hook, you can use `to_owned` to create a clone of the hook handle. You can then use that clone in the async closure.
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
{{#include ../../examples/spawn.rs:to_owned_macro}}
```
Calling `spawn` will give you a `JoinHandle` which lets you cancel or pause the future.
## Spawning Tokio Tasks
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
{{#include ../../examples/spawn.rs:tokio}}
```
+183
View File
@@ -0,0 +1,183 @@
# Coroutines
Another good tool to keep in your async toolbox are coroutines. Coroutines are futures that can be manually stopped, started, paused, and resumed.
Like regular futures, code in a Dioxus coroutine will run until the next `await` point before yielding. This low-level control over asynchronous tasks is quite powerful, allowing for infinitely looping tasks like WebSocket polling, background timers, and other periodic actions.
## `use_coroutine`
The basic setup for coroutines is the `use_coroutine` hook. Most coroutines we write will be polling loops using async/await.
```rust
fn app(cx: Scope) -> Element {
let ws: &UseCoroutine<()> = use_coroutine(&cx, |rx| async move {
// Connect to some sort of service
let mut conn = connect_to_ws_server().await;
// Wait for data on the service
while let Some(msg) = conn.next().await {
// handle messages
}
});
}
```
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
let sync: &UseCoroutine<()> = use_coroutine(&cx, |rx| async move {
// code for syncing
});
if sync.is_running() {
cx.render(rsx!{
button {
onclick: move |_| sync.pause(),
"Disable syncing"
}
})
} else {
cx.render(rsx!{
button {
onclick: move |_| sync.resume(),
"Enable syncing"
}
})
}
```
This pattern is where coroutines are extremely useful – instead of writing all the complicated logic for pausing our async tasks like we would with JavaScript promises, the Rust model allows us to just not poll our future.
## Sending Values
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 have the opportunity to centralize our async logic. The `rx` parameter is an Unbounded Channel for 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
enum ProfileUpdate {
SetUsername(String),
SetAge(i32)
}
let profile = use_coroutine(&cx, |mut rx: UnboundedReciver<ProfileUpdate>| async move {
let mut server = connect_to_server().await;
while let Ok(msg) = rx.next().await {
match msg {
ProfileUpdate::SetUsername(name) => server.update_username(name).await,
ProfileUpdate::SetAge(age) => server.update_age(age).await,
}
}
});
cx.render(rsx!{
button {
onclick: move |_| profile.send(ProfileUpdate::SetUsername("Bob".to_string())),
"Update username"
}
})
```
For sufficiently complex apps, we could build a bunch of different useful "services" that loop on channels to update the app.
```rust
let profile = use_coroutine(&cx, profile_service);
let editor = use_coroutine(&cx, editor_service);
let sync = use_coroutine(&cx, sync_service);
async fn profile_service(rx: UnboundedReceiver<ProfileCommand>) {
// do stuff
}
async fn sync_service(rx: UnboundedReceiver<SyncCommand>) {
// do stuff
}
async fn editor_service(rx: UnboundedReceiver<EditorCommand>) {
// do stuff
}
```
We can combine coroutines with Fermi 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
static USERNAME: Atom<String> = |_| "default".to_string();
fn app(cx: Scope) -> Element {
let atoms = use_atom_root(&cx);
use_coroutine(&cx, |rx| sync_service(rx, atoms.clone()));
cx.render(rsx!{
Banner {}
})
}
fn Banner(cx: Scope) -> Element {
let username = use_read(&cx, USERNAME);
cx.render(rsx!{
h1 { "Welcome back, {username}" }
})
}
```
Now, in our sync service, we can structure our state however we want. We only need to update the view values when ready.
```rust
enum SyncAction {
SetUsername(String),
}
async fn sync_service(mut rx: UnboundedReceiver<SyncAction>, atoms: AtomRoot) {
let username = atoms.write(USERNAME);
let errors = atoms.write(ERRORS);
while let Ok(msg) = rx.next().await {
match msg {
SyncAction::SetUsername(name) => {
if set_name_on_server(&name).await.is_ok() {
username.set(name);
} else {
errors.make_mut().push("SetUsernameFailed");
}
}
}
}
}
```
## Yielding Values
To yield values from a coroutine, simply bring in a `UseState` handle and set the value whenever your coroutine completes its work.
```rust
let sync_status = use_state(&cx, || Status::Launching);
let sync_task = use_coroutine(&cx, |rx: UnboundedReceiver<SyncAction>| {
to_owned![sync_status];
async move {
loop {
delay_ms(1000).await;
sync_status.set(Status::Working);
}
}
})
```
## Automatic injection into the Context API
Coroutine handles are automatically injected through the context API. `use_coroutine_handle` with the message type as a generic can be used to fetch a handle.
```rust
fn Child(cx: Scope) -> Element {
let sync_task = use_coroutine_handle::<SyncAction>(&cx);
sync_task.send(SyncAction::SetUsername);
}
```
+33
View File
@@ -0,0 +1,33 @@
# UseFuture
[`use_future`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_future.html) lets you run an async closure, and provides you with its result.
For example, we can make an API request inside `use_future`:
```rust
{{#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 then render that result:
```rust
{{#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.
## Dependencies
Often, you will need to run the future again every time some value (e.g. a prop) changes. Rather than `.restart`ing it 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
{{#include ../../examples/use_future.rs:dependency}}
```