mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 06:34:20 +00:00
docs: add more docs
This commit is contained in:
parent
bdf234d728
commit
1560e2daca
20 changed files with 259 additions and 44 deletions
1
docs/guide/.vscode/spellright.dict
vendored
1
docs/guide/.vscode/spellright.dict
vendored
|
@ -1,2 +1,3 @@
|
|||
oninput
|
||||
Webview
|
||||
idanarye
|
||||
|
|
|
@ -9,9 +9,10 @@
|
|||
- [Lists](elements/lists.md)
|
||||
- [Special Attributes](elements/special_attributes.md)
|
||||
- [Components](elements/components.md)
|
||||
- [Component Properties](elements/propsmacro.md)
|
||||
- [Reusing, Importing, and Exporting Components](elements/exporting_components.md)
|
||||
- [Component Children and Attributes](elements/component_children.md)
|
||||
- [Properties](elements/propsmacro.md)
|
||||
- [Reusing, Importing, and Exporting](elements/exporting_components.md)
|
||||
- [Children and Attributes](elements/component_children.md)
|
||||
- [Composing Components](elements/composing.md)
|
||||
- [Adding Interactivity](interactivity/index.md)
|
||||
- [Hooks and Internal State](interactivity/hooks.md)
|
||||
- [Event handlers](interactivity/event_handlers.md)
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
# Fetching
|
||||
|
||||
|
||||
This section is currently under construction! 🏗
|
||||
|
|
|
@ -17,3 +17,6 @@ Writing apps that deal with Send/Sync can be frustrating at times. Under the hoo
|
|||
|
||||
|
||||
All async code in your app is polled on a `LocalSet`, so any async code we w
|
||||
|
||||
|
||||
> This section is currently under construction! 🏗
|
||||
|
|
43
docs/guide/src/elements/composing.md
Normal file
43
docs/guide/src/elements/composing.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Composing Components
|
||||
|
||||
So far, we've talked about declaring new components and setting up their properties. However, we haven't really talked about how components work together and how your app is updated.
|
||||
|
||||
In this section, we'll talk about:
|
||||
|
||||
- Sharing data between components
|
||||
- How the UI is updated from input and state changes
|
||||
- Forcing renders
|
||||
- How renders propagate
|
||||
-
|
||||
|
||||
|
||||
### Rendering our posts with a PostList component
|
||||
|
||||
Let's start by modeling this problem with a component and some properties.
|
||||
|
||||
For this example, we're going to use the borrowed component syntax since we probably have a large list of posts that we don't want to clone every time we render the Post List.
|
||||
|
||||
```rust
|
||||
#[derive(Props, PartialEq)]
|
||||
struct PostListProps<'a> {
|
||||
posts: &'a [PostData]
|
||||
}
|
||||
```
|
||||
Next, we're going to define our component:
|
||||
|
||||
```rust
|
||||
fn App(cx: Scope<PostList>) -> Element {
|
||||
cx.render(rsx!{
|
||||
ul { class: "post-list",
|
||||
// we can drop an iterator directly into our elements
|
||||
cx.props.posts.iter().map(|post| rsx!{
|
||||
Post {
|
||||
title: post.title,
|
||||
age: post.age,
|
||||
original_poster: post.original_poster
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
|
@ -45,10 +45,21 @@ Finally, we can include this list in the final structure:
|
|||
```rust
|
||||
rsx!(
|
||||
ul {
|
||||
{name_list}
|
||||
name_list
|
||||
}
|
||||
)
|
||||
```
|
||||
Or, we can include the iterator inline:
|
||||
```rust
|
||||
rsx!(
|
||||
ul {
|
||||
names.iter().map(|name| rsx!(
|
||||
li { "{name}" }
|
||||
))
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
The HTML-rendered version of this list would follow what you would expect:
|
||||
```html
|
||||
<ul>
|
||||
|
@ -59,38 +70,6 @@ The HTML-rendered version of this list would follow what you would expect:
|
|||
</ul>
|
||||
```
|
||||
|
||||
### Rendering our posts with a PostList component
|
||||
|
||||
Let's start by modeling this problem with a component and some properties.
|
||||
|
||||
For this example, we're going to use the borrowed component syntax since we probably have a large list of posts that we don't want to clone every time we render the Post List.
|
||||
|
||||
```rust
|
||||
#[derive(Props, PartialEq)]
|
||||
struct PostListProps<'a> {
|
||||
posts: &'a [PostData]
|
||||
}
|
||||
```
|
||||
Next, we're going to define our component:
|
||||
|
||||
```rust
|
||||
fn App(cx: Scope<PostList>) -> Element {
|
||||
cx.render(rsx!{
|
||||
ul { class: "post-list",
|
||||
// we can drop an iterator directly into our elements
|
||||
cx.props.posts.iter().map(|post| rsx!{
|
||||
Post {
|
||||
title: post.title,
|
||||
age: post.age,
|
||||
original_poster: post.original_poster
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Filtering Iterators
|
||||
|
||||
Rust's iterators are extremely powerful, especially when used for filtering tasks. When building user interfaces, you might want to display a list of items filtered by some arbitrary check.
|
||||
|
|
|
@ -9,6 +9,151 @@ All component `properties` must implement the `Properties` trait. The `Props` ma
|
|||
|
||||
|
||||
|
||||
## Using the Props Macro
|
||||
|
||||
All `properties` that your components take must implement the `Properties` trait. The simplest props you can use is simply `()` - or no value at all. `Scope` is generic over your component's props and actually defaults to `()`.
|
||||
|
||||
```rust
|
||||
// this scope
|
||||
Scope<()>
|
||||
|
||||
// is the same as this scope
|
||||
Scope
|
||||
```
|
||||
|
||||
If we wanted to define a component with its own props, we would create a new struct and tack on the `Props` derive macro:
|
||||
|
||||
```rust
|
||||
#[derive(Props)]
|
||||
struct MyProps {
|
||||
name: String
|
||||
}
|
||||
```
|
||||
This particular code will not compile - all `Props` must either a) borrow from their parent or b) implement `PartialEq`. Since our props do not borrow from their parent, they are `'static` and must implement PartialEq.
|
||||
|
||||
For an owned example:
|
||||
```rust
|
||||
#[derive(Props, PartialEq)]
|
||||
struct MyProps {
|
||||
name: String
|
||||
}
|
||||
```
|
||||
|
||||
For a borrowed example:
|
||||
```rust
|
||||
#[derive(Props)]
|
||||
struct MyProps<'a> {
|
||||
name: &'a str
|
||||
}
|
||||
```
|
||||
|
||||
Then, to use these props in our component, we simply swap out the generic parameter on scope.
|
||||
|
||||
For owned props, we just drop it in:
|
||||
|
||||
```rust
|
||||
fn Demo(cx: Scope<MyProps>) -> Element {
|
||||
todo!()
|
||||
}
|
||||
```
|
||||
|
||||
However, for props that borrow data, we need to explicitly declare lifetimes. Rust does not know that our props and our component share the same lifetime, so must explicitly attach a lifetime in two places:
|
||||
|
||||
```rust
|
||||
fn Demo<'a>(cx: Scope<'a, MyProps<'a>>) -> Element {
|
||||
todo!()
|
||||
}
|
||||
```
|
||||
|
||||
By putting the `'a` lifetime on Scope and our Props, we can now borrow data from our parent and pass it on to our children.
|
||||
|
||||
|
||||
## Memoization
|
||||
|
||||
If you're coming from React, you might be wondering how memoization fits in. For our purpose, memoization is the process in which we check if a component actually needs to be re-rendered when its props change. If a component's properties change but they wouldn't necessarily affect the output, then we don't need to actually re-render the component.
|
||||
|
||||
For example, let's say we have a component that has two children:
|
||||
|
||||
```rust
|
||||
fn Demo(cx: Scope) -> Element {
|
||||
let name = use_state(&cx, || String::from("bob"));
|
||||
let age = use_state(&cx, || 21);
|
||||
|
||||
cx.render(rsx!{
|
||||
Name { name: name }
|
||||
Age { age: age }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
If `name` changes but `age` does not, then there is no reason to re-render our `Age` component since the contents of its props did not meaningfully change.
|
||||
|
||||
|
||||
Dioxus implements memoization by default, which means you can always rely on props with `PartialEq` or no props at all to act as barriers in your app. This can be extremely useful when building larger apps where properties frequently change. By moving our state into a global state management solution, we can achieve precise, surgical re-renders, improving the performance of our app.
|
||||
|
||||
|
||||
However, for components that borrow values from their parents, we cannot safely memoize them.
|
||||
|
||||
For example, this component borrows `&str` - and if the parent re-renders, then the actual reference to `str` will probably be different. Since the data is borrowed, we need to pass a new version down the tree.
|
||||
|
||||
```rust
|
||||
#[derive(Props)]
|
||||
struct MyProps<'a> {
|
||||
name: &'a str
|
||||
}
|
||||
|
||||
fn Demo<'a>(cx: Scope<'a, MyProps<'a>>) -> Element {
|
||||
todo!()
|
||||
}
|
||||
```
|
||||
|
||||
TLDR:
|
||||
- if you see props with a lifetime or generics, it cannot be memoized
|
||||
- memoization is done automatically through the `PartialEq` trait
|
||||
- components with empty props can act as memoization barriers
|
||||
|
||||
## Optional Fields
|
||||
|
||||
Dioxus' `Props` macro is very similar to [@idanarye](https://github.com/idanarye)'s [TypedBuilder crate](https://github.com/idanarye/rust-typed-builder) and supports many of the same parameters.
|
||||
|
||||
For example, you can easily create optional fields by attaching the `optional` modifier to a field.
|
||||
|
||||
```rust
|
||||
#[derive(Props, PartialEq)]
|
||||
struct MyProps {
|
||||
name: String,
|
||||
|
||||
#[props(optional)]
|
||||
description: Option<String>
|
||||
}
|
||||
|
||||
fn Demo(cx: MyProps) -> Element {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Then, we can completely omit the description field when calling the component:
|
||||
|
||||
```rust
|
||||
rsx!{
|
||||
Demo {
|
||||
name: "Thing".to_string(),
|
||||
// description is omitted
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `optional` modifier is a combination of two separate modifiers: `default` and `strip_option`. The full list of modifiers includes:
|
||||
|
||||
- `default` - automatically add the field using its `Default` implementation
|
||||
- `strip_option` - automatically wrap values at the call site in `Some`
|
||||
- `optional` - combine both `default` and `strip_option`
|
||||
- `into` - automatically call `into` on the value at the callsite
|
||||
|
||||
For more information on how tags work, check out the [TypedBuilder](https://github.com/idanarye/rust-typed-builder) crate. However, all attributes for props in Dioxus are flattened (no need for `setter` syntax) and the `optional` field is new.
|
||||
|
||||
|
||||
|
||||
|
||||
## The inline_props macro
|
||||
|
||||
|
|
|
@ -30,9 +30,7 @@ fn BlogPost(cx: Scope) -> Element {
|
|||
}
|
||||
```
|
||||
|
||||
> Note!
|
||||
|
||||
This attribute is called "dangerous_inner_html" because it is DANGEROUS. If you're not careful, you can easily expose cross-site-scripting (XSS) attacks to your users. If you're handling untrusted input, make sure to escape your HTML before passing it into `dangerous_inner_html`.
|
||||
> Note! This attribute is called "dangerous_inner_html" because it is DANGEROUS. If you're not careful, you can easily expose cross-site-scripting (XSS) attacks to your users. If you're handling untrusted input, make sure to escape your HTML before passing it into `dangerous_inner_html`.
|
||||
|
||||
|
||||
## Boolean Attributes
|
||||
|
@ -87,13 +85,13 @@ Not all attributes work like this however. Only *these specific attributes* are
|
|||
|
||||
For any other attributes, a value of `"false"` will be sent directly to the DOM.
|
||||
|
||||
## `prevent_default`
|
||||
## Stopping form input and navigation with `prevent_default`
|
||||
|
||||
Currently, preventing default on events from an event handler is not possible from Desktop/Mobile. Until this is supported, it's possible to prevent default using the `prevent_default` attribute.
|
||||
Currently, calling `prevent_default` on events in EventHandlers is not possible from Desktop/Mobile. Until this is supported, it's possible to prevent default using the `prevent_default` attribute.
|
||||
|
||||
> Note: you cannot conditionally prevent default with this approach. This is a limitation until synchronous event handling is available across the Webview boundary
|
||||
|
||||
To use `prevent_default`, simple use the `prevent_default` attribute and set it to the name of the event handler you want to prevent default on. We can attach this attribute multiple times for multiple attributes.
|
||||
To use `prevent_default`, simply attach the `prevent_default` attribute to a given element and set it to the name of the event handler you want to prevent default on. We can attach this attribute multiple times for multiple attributes.
|
||||
|
||||
```rust
|
||||
rsx!{
|
||||
|
@ -107,7 +105,7 @@ rsx!{
|
|||
}
|
||||
```
|
||||
|
||||
## `..Attributes`
|
||||
## Passing attributes into children: `..Attributes`
|
||||
|
||||
Just like Dioxus supports spreading component props into components, we also support spreading attributes into elements. This lets you pass any arbitrary attributes through components into elements.
|
||||
|
||||
|
@ -131,7 +129,6 @@ pub fn StateInput<'a>(cx: Scope<'a, InputProps<'a>>) -> Element {
|
|||
|
||||
## Controlled inputs and `value`, `checked`, and `selected`
|
||||
|
||||
|
||||
In Dioxus, there is a distinction between controlled and uncontrolled inputs. Most inputs you'll use are "controlled," meaning we both drive the `value` of the input and react to the `oninput`.
|
||||
|
||||
Controlled components:
|
||||
|
@ -179,3 +176,15 @@ rsx!{
|
|||
```
|
||||
|
||||
## Wrapping up
|
||||
|
||||
We've reached just about the end of what you can do with elements without venturing into "advanced" territory.
|
||||
|
||||
In this chapter, we learned:
|
||||
- How to declare elements
|
||||
- How to conditionally render parts of your UI
|
||||
- How to render lists
|
||||
- Which attributes are "special"
|
||||
|
||||
There's more to elements! For further reading, check out:
|
||||
|
||||
- [Custom Elements]()
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
# Event handlers
|
||||
|
||||
> This section is currently under construction! 🏗
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
# Lifecycle, updates, and effects
|
||||
|
||||
> This section is currently under construction! 🏗
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
# User Input and Controlled Components
|
||||
|
||||
Handling user input is one of the most common things your app will do, but it can be tricky
|
||||
|
||||
|
||||
> This section is currently under construction! 🏗
|
||||
|
|
|
@ -9,3 +9,4 @@ fn App((cx, props): Component) -> Element {
|
|||
}
|
||||
```
|
||||
|
||||
> This section is currently under construction! 🏗
|
||||
|
|
|
@ -7,6 +7,7 @@ In this chapter, we'll cover the various ways to manage state, the appropriate t
|
|||
## Terminology
|
||||
|
||||
|
||||
> This section is currently under construction! 🏗
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
# Lifting State
|
||||
|
||||
|
||||
> This section is currently under construction! 🏗
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
# Local State
|
||||
|
||||
|
||||
> This section is currently under construction! 🏗
|
||||
|
|
|
@ -1 +1,7 @@
|
|||
# Global State
|
||||
|
||||
|
||||
cx.provide_context()
|
||||
cx.consume_context()
|
||||
|
||||
> This section is currently under construction! 🏗
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
# Defining Components
|
||||
|
||||
This section is currently under construction! 🏗
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
# Defining State
|
||||
|
||||
This section is currently under construction! 🏗
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
# Structuring our app
|
||||
|
||||
|
||||
This section is currently under construction! 🏗
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
# Styling
|
||||
|
||||
|
||||
This section is currently under construction! 🏗
|
||||
|
|
Loading…
Reference in a new issue