docs: add more docs

This commit is contained in:
Jonathan Kelley 2022-01-11 01:11:47 -05:00
parent bdf234d728
commit 1560e2daca
20 changed files with 259 additions and 44 deletions

View file

@ -1,2 +1,3 @@
oninput
Webview
idanarye

View file

@ -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)

View file

@ -1 +1,4 @@
# Fetching
This section is currently under construction! 🏗

View file

@ -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! 🏗

View 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
}
})
}
})
}
```

View file

@ -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.

View file

@ -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

View file

@ -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]()

View file

@ -1 +1,3 @@
# Event handlers
> This section is currently under construction! 🏗

View file

@ -1 +1,3 @@
# Lifecycle, updates, and effects
> This section is currently under construction! 🏗

View file

@ -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! 🏗

View file

@ -9,3 +9,4 @@ fn App((cx, props): Component) -> Element {
}
```
> This section is currently under construction! 🏗

View file

@ -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! 🏗

View file

@ -1 +1,4 @@
# Lifting State
> This section is currently under construction! 🏗

View file

@ -1 +1,4 @@
# Local State
> This section is currently under construction! 🏗

View file

@ -1 +1,7 @@
# Global State
cx.provide_context()
cx.consume_context()
> This section is currently under construction! 🏗

View file

@ -1 +1,3 @@
# Defining Components
This section is currently under construction! 🏗

View file

@ -1 +1,3 @@
# Defining State
This section is currently under construction! 🏗

View file

@ -1 +1,4 @@
# Structuring our app
This section is currently under construction! 🏗

View file

@ -1 +1,4 @@
# Styling
This section is currently under construction! 🏗