docs: add more docs
This commit is contained in:
1 parent
bdf234d728
commit
1560e2daca
20 files changed
+259
-44
No files matched your search
Vendored
+1
@@ -1,2 +1,3 @@
|
|||||||
oninput
|
oninput
|
||||||
Webview
|
Webview
|
||||||
|
idanarye
|
||||||
@@ -9,9 +9,10 @@
|
|||||||
- [Lists](elements/lists.md)
|
- [Lists](elements/lists.md)
|
||||||
- [Special Attributes](elements/special_attributes.md)
|
- [Special Attributes](elements/special_attributes.md)
|
||||||
- [Components](elements/components.md)
|
- [Components](elements/components.md)
|
||||||
- [Component Properties](elements/propsmacro.md)
|
- [Properties](elements/propsmacro.md)
|
||||||
- [Reusing, Importing, and Exporting Components](elements/exporting_components.md)
|
- [Reusing, Importing, and Exporting](elements/exporting_components.md)
|
||||||
- [Component Children and Attributes](elements/component_children.md)
|
- [Children and Attributes](elements/component_children.md)
|
||||||
|
- [Composing Components](elements/composing.md)
|
||||||
- [Adding Interactivity](interactivity/index.md)
|
- [Adding Interactivity](interactivity/index.md)
|
||||||
- [Hooks and Internal State](interactivity/hooks.md)
|
- [Hooks and Internal State](interactivity/hooks.md)
|
||||||
- [Event handlers](interactivity/event_handlers.md)
|
- [Event handlers](interactivity/event_handlers.md)
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
# Fetching
|
# 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
|
All async code in your app is polled on a `LocalSet`, so any async code we w
|
||||||
|
|
||||||
|
|
||||||
|
> This section is currently under construction! 🏗
|
||||||
@@ -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
|
```rust
|
||||||
rsx!(
|
rsx!(
|
||||||
ul {
|
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:
|
The HTML-rendered version of this list would follow what you would expect:
|
||||||
```html
|
```html
|
||||||
<ul>
|
<ul>
|
||||||
@@ -59,38 +70,6 @@ The HTML-rendered version of this list would follow what you would expect:
|
|||||||
</ul>
|
</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
|
## 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.
|
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
|
## The inline_props macro
|
||||||
|
|
||||||
|
|||||||
@@ -30,9 +30,7 @@ fn BlogPost(cx: Scope) -> Element {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> Note!
|
> 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`.
|
||||||
|
|
||||||
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
|
## 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.
|
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
|
> 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
|
```rust
|
||||||
rsx!{
|
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.
|
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`
|
## 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`.
|
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:
|
Controlled components:
|
||||||
@@ -179,3 +176,15 @@ rsx!{
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Wrapping up
|
## 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
|
# Event handlers
|
||||||
|
|
||||||
|
> This section is currently under construction! 🏗
|
||||||
@@ -1 +1,3 @@
|
|||||||
# Lifecycle, updates, and effects
|
# Lifecycle, updates, and effects
|
||||||
|
|
||||||
|
> This section is currently under construction! 🏗
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
# User Input and Controlled Components
|
# User Input and Controlled Components
|
||||||
|
|
||||||
Handling user input is one of the most common things your app will do, but it can be tricky
|
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
|
## Terminology
|
||||||
|
|
||||||
|
|
||||||
|
> This section is currently under construction! 🏗
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
# Lifting State
|
# Lifting State
|
||||||
|
|
||||||
|
|
||||||
|
> This section is currently under construction! 🏗
|
||||||
@@ -1 +1,4 @@
|
|||||||
# Local State
|
# Local State
|
||||||
|
|
||||||
|
|
||||||
|
> This section is currently under construction! 🏗
|
||||||
@@ -1 +1,7 @@
|
|||||||
# Global State
|
# Global State
|
||||||
|
|
||||||
|
|
||||||
|
cx.provide_context()
|
||||||
|
cx.consume_context()
|
||||||
|
|
||||||
|
> This section is currently under construction! 🏗
|
||||||
@@ -1 +1,3 @@
|
|||||||
# Defining Components
|
# Defining Components
|
||||||
|
|
||||||
|
This section is currently under construction! 🏗
|
||||||
@@ -1 +1,3 @@
|
|||||||
# Defining State
|
# Defining State
|
||||||
|
|
||||||
|
This section is currently under construction! 🏗
|
||||||
@@ -1 +1,4 @@
|
|||||||
# Structuring our app
|
# Structuring our app
|
||||||
|
|
||||||
|
|
||||||
|
This section is currently under construction! 🏗
|
||||||
@@ -1 +1,4 @@
|
|||||||
# Styling
|
# Styling
|
||||||
|
|
||||||
|
|
||||||
|
This section is currently under construction! 🏗
|
||||||
Reference in new issue
Block a user