Update docs to reflect changes in master (#670)

* work on updating docs to master version

* more updates

* more polishing

* finish interactivity chapter

* finish updating core guide

* fix grammer mistakes and typos

* more grammer fixes

* add liveview guide

* remove doc build

* WIP custom renderer docs

* add axum as dev-dependancy to guide

* fix examples

* fix overview example

* use md book fork to fix compilation
This commit is contained in:
ealmloff authored and GitHub committed 2023-01-06 15:00:12 -08:00
1 parent a616a8fa9d
commit cd4474cc4f
51 files changed
+870 -549

No files matched your search

@@ -12,10 +12,19 @@ To render different elements based on a condition, you could use an `if-else` st
> You could also use `match` statements, or any Rust function to conditionally render different things.
### Improving the `if-else` Example
You may have noticed some repeated code in the `if-else` example above. Repeating code like this is both bad for maintainability and performance. Dioxus will skip diffing static elements like the button, but when switching between multiple `rsx` calls it cannot perform this optimization. For this example either approach is fine, but for components with large parts that are reused between conditionals, it can be more of an issue.
We can improve this example by splitting up the dynamic parts and inserting them where they are needed.
```rust
{{#include ../../../examples/conditional_rendering.rs:if_else_improved}}
```
### Inspecting `Element` props
Since `Element` is a `Option<VNode>`, components accepting `Element` as a prop can actually inspect its contents, and render different things based on that. Example:
Since `Element` is a `Option<VNode>`, components accepting `Element` as a prop can inspect its contents, and render different things based on that. Example:
```rust
{{#include ../../../examples/component_children_inspect.rs:Clickable}}
@@ -43,9 +52,9 @@ Often, you'll want to render a collection of components. For example, you might
For this, Dioxus accepts iterators that produce `Element`s. So we need to:
- Get an iterator over all of our items (e.g., if you have a `Vec` of comments, iterate over it with `iter()`)
- `.map` the iterator to convert each item into a rendered `Element` using `cx.render(rsx!(...))`
- `.map` the iterator to convert each item into a `LazyNode` using `rsx!(...)`
- Add a unique `key` attribute to each iterator item
- Include this iterator in the final RSX
- Include this iterator in the final RSX (or use it inline)
Example: suppose you have a list of comments you want to render. Then, you can render them like this:
@@ -53,21 +62,26 @@ Example: suppose you have a list of comments you want to render. Then, you can r
{{#include ../../../examples/rendering_lists.rs:render_list}}
```
### Inline for loops
Because of how common it is to render a list of items, Dioxus provides a shorthand for this. Instead of using `.iter, `.map`, and `rsx`, you can use a `for` loop with a body of rsx code:
```rust
{{#include ../../../examples/rendering_lists.rs:render_list_for_loop}}
```
### The `key` Attribute
Every time you re-render your list, Dioxus needs to keep track of which item went where, because the order of items in a list might change – items might be added, removed or swapped. Despite that, Dioxus needs to:
- Keep track of component state
- Efficiently figure out what updates need to be made to the UI
Every time you re-render your list, Dioxus needs to keep track of which items go where to determine what updates need to be made to the UI.
For example, suppose the `CommentComponent` had some state – e.g. a field where the user typed in a reply. If the order of comments suddenly changes, Dioxus needs to correctly associate that state with the same comment – otherwise, the user will end up replying to a different comment!
To help Dioxus keep track of list items, we need to associate each item with a unique key. In the example above, we dynamically generated the unique key. In real applications, it's more likely that the key will come from e.g. a database ID. It doesn't really matter where you get the key from, as long as it meets the requirements
To help Dioxus keep track of list items, we need to associate each item with a unique key. In the example above, we dynamically generated the unique key. In real applications, it's more likely that the key will come from e.g. a database ID. It doesn't matter where you get the key from, as long as it meets the requirements:
- Keys must be unique in a list
- The same item should always get associated with the same key
- Keys should be relatively small (i.e. converting the entire Comment structure to a String would be a pretty bad key) so they can be compared efficiently
You might be tempted to use an item's index in the list as its key. In fact, that’s what Dioxus will use if you don’t specify a key at all. This is only acceptable if you can guarantee that the list is constant – i.e., no re-ordering, additions or deletions.
You might be tempted to use an item's index in the list as its key. That’s what Dioxus will use if you don’t specify a key at all. This is only acceptable if you can guarantee that the list is constant – i.e., no re-ordering, additions, or deletions.
> Note that if you pass the key to a component you've made, it won't receive the key as a prop. It’s only used as a hint by Dioxus itself. If your component needs an ID, you have to pass it as a separate prop.
@@ -1,10 +1,10 @@
# Event Handlers
Events are interesting things that happen, usually related to something the user has done. For example, some events happen when the user clicks, scrolls, moves the mouse, or types a character. To respond to these actions and make the UI interactive, we need to handle those events.
Event handlers are used to respond to user actions. For example, an event handler could be triggered when the user clicks, scrolls, moves the mouse, or types a character.
Events are associated with elements. For example, we usually don't care about all the clicks that happen within an app, only those on a particular button. To handle events that happen on an element, we must attach the desired event handler to it.
Event handlers are attached to elements. For example, we usually don't care about all the clicks that happen within an app, only those on a particular button.
Event handlers are similar to regular attributes, but their name usually starts with `on`- and they accept closures as values. The closure will be called whenever the event is triggered, and will be passed that event.
Event handlers are similar to regular attributes, but their name usually starts with `on`- and they accept closures as values. The closure will be called whenever the event it listens for is triggered and will be passed that event.
For example, to handle clicks on an element, we can specify an `onclick` handler:
@@ -14,20 +14,24 @@ For example, to handle clicks on an element, we can specify an `onclick` handler
## The `Event` object
Event handlers receive an [`UiEvent`](https://docs.rs/dioxus-core/latest/dioxus_core/struct.UiEvent.html) object containing information about the event. Different types of events contain different types of data. For example, mouse-related events contain [`MouseData`](https://docs.rs/dioxus/latest/dioxus/events/struct.MouseData.html), which tells you things like where the mouse was clicked and what mouse buttons were used.
Event handlers receive an [`Event`](https://docs.rs/dioxus-core/latest/dioxus_core/struct.Event.html) object containing information about the event. Different types of events contain different types of data. For example, mouse-related events contain [`MouseData`](https://docs.rs/dioxus/latest/dioxus/events/struct.MouseData.html), which tells you things like where the mouse was clicked and what mouse buttons were used.
In the example above, this event data was logged to the terminal:
```
Clicked! Event: UiEvent { data: MouseData { coordinates: Coordinates { screen: (468.0, 109.0), client: (73.0, 25.0), element: (63.0, 15.0), page: (73.0, 25.0) }, modifiers: (empty), held_buttons: EnumSet(), trigger_button: Some(Primary) } }
Clicked! Event: UiEvent { data: MouseData { coordinates: Coordinates { screen: (468.0, 109.0), client: (73.0, 25.0), element: (63.0, 15.0), page: (73.0, 25.0) }, modifiers: (empty), held_buttons: EnumSet(), trigger_button: Some(Primary) } }
Clicked! Event: UiEvent { bubble_state: Cell { value: true }, data: MouseData { coordinates: Coordinates { screen: (242.0, 256.0), client: (26.0, 17.0), element: (16.0, 7.0), page: (26.0, 17.0) }, modifiers: (empty), held_buttons: EnumSet(), trigger_button: Some(Primary) } }
Clicked! Event: UiEvent { bubble_state: Cell { value: true }, data: MouseData { coordinates: Coordinates { screen: (242.0, 256.0), client: (26.0, 17.0), element: (16.0, 7.0), page: (26.0, 17.0) }, modifiers: (empty), held_buttons: EnumSet(), trigger_button: Some(Primary) } }
```
To learn what the different event types provide, read the [events module docs](https://docs.rs/dioxus/latest/dioxus/events/index.html).
To learn what the different event types for HTML provide, read the [events module docs](https://docs.rs/dioxus-html/latest/dioxus_html/events/index.html).
### Stopping propagation
### Event propagation
When you have e.g. a `button` inside a `div`, any click on the `button` is also a click on the `div`. For this reason, Dioxus propagates the click event: first, it is triggered on the target element, then on parent elements. If you want to prevent this behavior, you can call `cancel_bubble()` on the event:
Some events will trigger first on the element the event originated at upward. For example, a click event on a `button` inside a `div` would first trigger the button's event listener and then the div's event listener.
> For more information about event propigation see [the mdn docs on event bubling](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_bubbling)
If you want to prevent this behavior, you can call `stop_propogation()` on the event:
```rust
{{#include ../../../examples/event_nested.rs:rsx}}
@@ -45,7 +49,7 @@ In some instances, might want to avoid this default behavior. For this, you can
Any event handlers will still be called.
> Normally, in React or JavaScript, you'd call "preventDefault" on the event in the callback. Dioxus does *not* currently support this behavior. Note: this means you cannot conditionally prevent default behavior.
> Normally, in React or JavaScript, you'd call "preventDefault" on the event in the callback. Dioxus does *not* currently support this behavior. Note: this means you cannot conditionally prevent default behavior based on the data in the event.
## Handler Props
+8 -8
View File
@@ -1,12 +1,12 @@
# Hooks and Component State
So far our components, being Rust functions, had no state – they were always rendering the same thing. However, in a UI component, it is often useful to have stateful functionality to build user interactions. For example, you might want to track whether the user has openend a drop-down, and render different things accordingly.
So far our components have had no state like a normal rust functions. However, in a UI component, it is often useful to have stateful functionality to build user interactions. For example, you might want to track whether the user has opened a drop-down, and render different things accordingly.
For stateful logic, you can use hooks. Hooks are Rust functions that take a reference to `ScopeState` (in a component, you can pass `&cx`), and provide you with functionality and state.
Hooks allow us to create state in our components. Hooks are Rust functions that take a reference to `ScopeState` (in a component, you can pass `cx`), and provide you with functionality and state.
## `use_state` Hook
[`use_state`](https://docs.rs/dioxus/latest/dioxus/hooks/fn.use_state.html) is one of the simplest hooks.
[`use_state`](https://docs.rs/dioxus/latest/dioxus/prelude/fn.use_state.html) is one of the simplest hooks.
- You provide a closure that determines the initial value
- `use_state` gives you the current value, and a way to update it by setting it to something else
@@ -21,7 +21,7 @@ For example, you might have seen the counter example, in which state (a number)
Every time the component's state changes, it re-renders, and the component function is called, so you can describe what you want the new UI to look like. You don't have to worry about "changing" anything – just describe what you want in terms of the state, and Dioxus will take care of the rest!
> `use_state` returns your value wrapped in a smart pointer of type [`UseState`](https://docs.rs/dioxus/latest/dioxus/hooks/struct.UseState.html). This is why you can both read the value and update it, even within a handler.
> `use_state` returns your value wrapped in a smart pointer of type [`UseState`](https://docs.rs/dioxus/latest/dioxus/prelude/struct.UseState.html). This is why you can both read the value and update it, even within an event handler.
You can use multiple hooks in the same component if you want:
@@ -40,13 +40,13 @@ But how can Dioxus differentiate between multiple hooks in the same component? A
{{#include ../../../examples/hooks_counter_two_state.rs:use_state_calls}}
```
This is only possible because the two hooks are always called in the same order, so Dioxus knows which is which. So the order you call hooks matters, which is why you must follow certain rules when using hooks:
This is only possible because the two hooks are always called in the same order, so Dioxus knows which is which. Because the order you call hooks matters, you must follow certain rules when using hooks:
1. Hooks may be only used in components or other hooks (we'll get to that later)
2. On every call to the component function
1. The same hooks must be called
2. In the same order
3. Hooks name's must start with `use_` so you don't accidentally confuse them with regular functions
3. Hooks name's should start with `use_` so you don't accidentally confuse them with regular functions
These rules mean that there are certain things you can't do with hooks:
@@ -73,11 +73,11 @@ For example, suppose we want to maintain a `Vec` of values. If we stored it with
Thankfully, there is another hook for that, `use_ref`! It is similar to `use_state`, but it lets you get a mutable reference to the contained data.
Here's a simple example that keeps a list of events in a `use_ref`. We can acquire write access to the state with `.write()`, and then just `.push` a new value to the state:
Here's a simple example that keeps a list of events in a `use_ref`. We can acquire write access to the state with `.with_mut()`, and then just `.push` a new value to the state:
```rust
{{#include ../../../examples/hooks_use_ref.rs:component}}
```
> The return values of `use_state` and `use_ref`, (`UseState` and `UseRef`, respectively) are in some ways similar to [`Cell`](https://doc.rust-lang.org/std/cell/) and [`RefCell`](https://doc.rust-lang.org/std/cell/struct.RefCell.html) – they provide interior mutability. However, these Dioxus wrappers also ensure that the component gets re-rendered whenever you change the state.
> The return values of `use_state` and `use_ref` (`UseState` and `UseRef`, respectively) are in some ways similar to [`Cell`](https://doc.rust-lang.org/std/cell/) and [`RefCell`](https://doc.rust-lang.org/std/cell/struct.RefCell.html) – they provide interior mutability. However, these Dioxus wrappers also ensure that the component gets re-rendered whenever you change the state.
+12 -12
View File
@@ -1,26 +1,23 @@
# Router
In many of your apps, you'll want to have different "scenes". For a webpage, these scenes might be the different webpages with their own content.
In many of your apps, you'll want to have different "scenes". For a webpage, these scenes might be the different webpages with their own content. For a desktop app, these scenes might be different views in your app.
You could write your own scene management solution – quite simply too. However, to save you the effort, Dioxus supports a first-party solution for scene management called Dioxus Router.
To unify these platforms, Dioxus provides a first-party solution for scene management called Dioxus Router.
## What is it?
For an app like the Dioxus landing page (https://dioxuslabs.com), we want to have different pages. A quick sketch of an app would be something like:
For an app like the Dioxus landing page (https://dioxuslabs.com), we want to have several different scenes:
- Homepage
- Blog
- Example showcase
Each of these scenes is independent – we don't want to render both the homepage and blog at the same time.
This is where the router crates come in handy. To make sure we're using the router, simply add the `"router"` feature to your dioxus dependency.
The Dioxus router makes it easy to create these scenes. To make sure we're using the router, add the `dioxus-router` package to your `Cargo.toml`.
```toml
[dependencies]
dioxus = { version = "*" }
dioxus-router = { version = "*" }
```shell
cargo add dioxus-router
```
@@ -30,8 +27,11 @@ Unlike other routers in the Rust ecosystem, our router is built declaratively. T
```rust
rsx!{
// All of our routes will be rendered inside this Router component
Router {
// if the current location is "/home", render the Home component
Route { to: "/home", Home {} }
// if the current location is "/blog", render the Blog component
Route { to: "/blog", Blog {} }
}
}
@@ -48,6 +48,7 @@ rsx!{
Router {
Route { to: "/home", Home {} }
Route { to: "/blog", Blog {} }
// if the current location doesn't match any of the above routes, render the NotFound component
Route { to: "", NotFound {} }
}
}
@@ -61,6 +62,7 @@ rsx!{
Router {
Route { to: "/home", Home {} }
Route { to: "/blog", Blog {} }
// if the current location doesn't match any of the above routes, redirect to "/home"
Redirect { from: "", to: "/home" }
}
}
@@ -82,6 +84,4 @@ rsx!{
## More reading
This page is just meant to be a very brief overview of the router to show you that there's a powerful solution already built for a very common problem. For more information about the router, definitely check out its book or check out some of the examples.
The router has its own documentation! [Available here](https://dioxuslabs.com/router/guide/).
This page is just a very brief overview of the router. For more information, check out [the router book](https://dioxuslabs.com/router/guide/) or some of [the router examples](https://github.com/DioxusLabs/dioxus/blob/master/examples/router.rs).
@@ -6,9 +6,9 @@ Often, multiple components need to access the same state. Depending on your need
One approach to share state between components is to "lift" it up to the nearest common ancestor. This means putting the `use_state` hook in a parent component, and passing the needed values down as props.
For example, suppose we want to build a meme editor. We want to have an input to edit the meme caption, but also a preview of the meme with the caption. Logically, the meme and the input are 2 separate components, but they need access to the same state (the current caption).
Suppose we want to build a meme editor. We want to have an input to edit the meme caption, but also a preview of the meme with the caption. Logically, the meme and the input are 2 separate components, but they need access to the same state (the current caption).
> 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 and easier to maintain (this is even more important for larger, complex apps).
> 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
@@ -57,7 +57,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)`, which you can call `.read` or `.write` on, similarly to `UseRef`. Otherwise, the value is `None`.
> `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`.
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
@@ -19,7 +19,7 @@ Notice the flexibility – you can:
## Uncontrolled Inputs
As an alternative to controlled inputs, you can simply let the platform keep track of the input values. If we don't tell a HTML input what content it should have, it be editable anyway (this is built into the webview). This approach can be more performant, but less flexible. For example, it's harder to keep the input in sync with another element.
As an alternative to controlled inputs, you can simply let the platform keep track of the input values. If we don't tell a HTML input what content it should have, it will be editable anyway (this is built into the browser). This approach can be more performant, but less flexible. For example, it's harder to keep the input in sync with another element.
Since you don't necessarily have the current value of the uncontrolled input in state, you can access it either by listening to `oninput` events (similarly to controlled components), or, if the input is part of a form, you can access the form data in the form events (e.g. `oninput` or `onsubmit`):