From 0f87ebb27789bf0e5af985295410401f382db9c0 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 6 Mar 2022 20:37:57 -0500 Subject: [PATCH] docs: even more docs --- docs/guide/src/SUMMARY.md | 25 ++- docs/guide/src/async/coroutines.md | 1 + docs/guide/src/async/index.md | 96 ++++++++++- docs/guide/src/async/loading_state.md | 8 + docs/guide/src/async/sockets.md | 1 + docs/guide/src/async/use_future.md | 1 + .../src/components/component_children.md | 2 + docs/guide/src/components/composing.md | 26 +-- docs/guide/src/helpers/index.md | 1 + .../guide/src/interactivity/event_handlers.md | 117 ++++++++++++- .../guide/src/interactivity/importanthooks.md | 117 ------------- docs/guide/src/interactivity/lifecycles.md | 12 +- docs/guide/src/interactivity/user_input.md | 82 +++++++++- docs/guide/src/interactivity/useref.md | 154 ++++++++++++++++++ docs/guide/src/interactivity/usestate.md | 114 +++++++++++++ docs/guide/src/state/channels.md | 1 + docs/guide/src/state/fanout.md | 61 +++++++ docs/guide/src/state/fermi.md | 110 +++++++++++++ docs/guide/src/state/index.md | 45 +++-- docs/guide/src/state/localstate.md | 60 ++++++- docs/guide/src/state/router.md | 86 ++++++++++ examples/crm.rs | 6 +- examples/tasks.rs | 4 +- packages/desktop/src/readme.md | 6 +- packages/hooks/src/usestate.rs | 24 +++ 25 files changed, 986 insertions(+), 174 deletions(-) create mode 100644 docs/guide/src/async/coroutines.md create mode 100644 docs/guide/src/async/loading_state.md create mode 100644 docs/guide/src/async/sockets.md create mode 100644 docs/guide/src/async/use_future.md create mode 100644 docs/guide/src/helpers/index.md create mode 100644 docs/guide/src/interactivity/useref.md create mode 100644 docs/guide/src/interactivity/usestate.md create mode 100644 docs/guide/src/state/channels.md create mode 100644 docs/guide/src/state/fanout.md create mode 100644 docs/guide/src/state/fermi.md create mode 100644 docs/guide/src/state/router.md diff --git a/docs/guide/src/SUMMARY.md b/docs/guide/src/SUMMARY.md index fff21fd02..e268fcd2d 100644 --- a/docs/guide/src/SUMMARY.md +++ b/docs/guide/src/SUMMARY.md @@ -15,26 +15,39 @@ - [Children and Attributes](components/component_children.md) - [How Data Flows](components/composing.md) - [Adding Interactivity](interactivity/index.md) - - [Hooks and Internal State](interactivity/hooks.md) - - [UseState and UseRef](interactivity/importanthooks.md) - [Event Listeners](interactivity/event_handlers.md) - - [User Input and Controlled Components](interactivity/user_input.md) - - [Lifecycle, updates, and effects](interactivity/lifecycles.md) + - [Hooks](interactivity/hooks.md) + - [UseState](interactivity/usestate.md) + - [UseRef](interactivity/useref.md) + - [User Input](interactivity/user_input.md) + - [Managing State](state/index.md) - [Local State](state/localstate.md) - [Lifting State](state/liftingstate.md) - [Global State](state/sharedstate.md) + - [Fanning Out](state/fanout.md) - [Error handling](state/errorhandling.md) +- [Helper Crates](helpers/index.md) + - [Fermi](state/fermi.md) + - [Router](state/router.md) - [Working with Async](async/index.md) - - [Tasks](async/asynctasks.md) + - [Updating State](async/loading_state.md) - [Fetching](async/fetching.md) + - [UseFuture](async/use_future.md) + - [UseCoroutine](async/coroutines.md) + - [WebSockets](async/sockets.md) + + + + + - [Next Steps and Advanced Topics](final.md) diff --git a/docs/guide/src/async/coroutines.md b/docs/guide/src/async/coroutines.md new file mode 100644 index 000000000..22f212de5 --- /dev/null +++ b/docs/guide/src/async/coroutines.md @@ -0,0 +1 @@ +# Coroutines diff --git a/docs/guide/src/async/index.md b/docs/guide/src/async/index.md index 0ec02e57d..b0abb494f 100644 --- a/docs/guide/src/async/index.md +++ b/docs/guide/src/async/index.md @@ -4,19 +4,101 @@ Not all apps you'll build can be self-contained with synchronous code. You'll of So far, we've only talked about building apps with synchronous code, so this chapter will focus integrating asynchronous code into your app. - ## The Runtime -By default, Dioxus-Desktop ships with the `Tokio` runtime and automatically sets everything up for you. - - +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. ## Send/Sync -Writing apps that deal with Send/Sync can be frustrating at times. Under the hood, Dioxus is not currently thread-safe, so any async code you write does *not* need to be `Send/Sync`. That means Cell/Rc/RefCell are all fair game. +Writing apps that deal with Send/Sync can be frustrating at times. Under the hood, 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`. + +All async code in your app is polled on a `LocalSet`, so you can also use `tokio::spawn_local`. + +## Spawning a future + +Currently, all futures in Dioxus must be `'static`. To spawn a future, simply call `cx.spawn()`. + +```rust +rsx!{ + button { + onclick: move |_| cx.spawn(async move { + let result = fetch_thing().await; + }) + } +} +``` + +Calling `spawn` will give you a `JoinHandle` which lets you cancel or pause the future. +## Setting state from within a future -All async code in your app is polled on a `LocalSet`, so any async code we w +If you start to write some async code, you'll quickly be greeted with the infamous error about borrowed items in static closures. +``` +error[E0759]: `cx` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement + --> examples/tasks.rs:13:27 + | +12 | fn app(cx: Scope) -> Element { + | ----- this data with an anonymous lifetime `'_`... +13 | let count = use_state(&cx, || 0); + | ^^^ ...is used here... +14 | +15 | use_future(&cx, (), move |_| { + | ---------- ...and is required to live as long as `'static` here + | +note: `'static` lifetime requirement introduced by this bound + --> /Users/jonkelley/Development/dioxus/packages/hooks/src/usefuture.rs:25:29 + | +25 | F: Future + 'static, + | ^^^^^^^ + +For more information about this error, try `rustc --explain E0759`. +error: could not compile `dioxus` due to previous error +``` + +Rustc tends to provide great feedback in its errors, but this particular error is actually quite unhelpful. For reference, here's our code: + +```rust +fn app(cx: Scope) -> Element { + let count = use_state(&cx, || 0); + + cx.spawn(async move { + tokio::time::sleep(Duration::from_millis(1000)).await; + count += 1; + }); + + cx.render(rsx! { + button { + onclick: move |_| count.set(0), + "Reset the count" + } + }) +} +``` + +Simple, right? We spawn a future that updates the value after one second has passed. Well, yes, and no. Our `count` value is only valid for the lifetime of this component, but our future could still be running even after the component re-renders. By default, Dioxus places a requirement on all futures to be `'static`, meaning they can't just borrow state from our hooks outright. + +To get around this, we need to get a `'static` handle to our state. All Dioxus hooks implement `Clone`, so you simply need to call clone before spawning your future. Any time you get the particular error above, make sure you call `Clone` or `ToOwned`. + +```rust +cx.spawn({ + let mut count = count.clone(); + async move { + tokio::time::sleep(Duration::from_millis(1000)).await; + count += 1; + } +}); +``` + +To make this a little bit easier, 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 +cx.spawn({ + to_owned![count, age, name, description, etc]; + async move { + tokio::time::sleep(Duration::from_millis(1000)).await; + count += 1; + } +}); +``` -> This section is currently under construction! 🏗 diff --git a/docs/guide/src/async/loading_state.md b/docs/guide/src/async/loading_state.md new file mode 100644 index 000000000..66ea79ba9 --- /dev/null +++ b/docs/guide/src/async/loading_state.md @@ -0,0 +1,8 @@ +# Updating State + +After your future has been spawned, you might want to update the state of our UI. Possible implementations might include: + +- A loading state +- A transition state +- Showing failures +- Listening to sockets and updating the app diff --git a/docs/guide/src/async/sockets.md b/docs/guide/src/async/sockets.md new file mode 100644 index 000000000..7627a1570 --- /dev/null +++ b/docs/guide/src/async/sockets.md @@ -0,0 +1 @@ +# WebSockets diff --git a/docs/guide/src/async/use_future.md b/docs/guide/src/async/use_future.md new file mode 100644 index 000000000..8d2c28d5b --- /dev/null +++ b/docs/guide/src/async/use_future.md @@ -0,0 +1 @@ +# UseFuture diff --git a/docs/guide/src/components/component_children.md b/docs/guide/src/components/component_children.md index 1259422af..d425132ba 100644 --- a/docs/guide/src/components/component_children.md +++ b/docs/guide/src/components/component_children.md @@ -137,6 +137,8 @@ fn clickable<'a>(cx: Scope<'a, ClickableProps<'a>>) -> Element { } ``` +Passing nodes through props means that they are immutable. If you find yourself needing to mutate nodes passed through props, consider creating a new node in its place that takes on its attributes, children, and listeners. +