mirror of
https://github.com/DioxusLabs/dioxus
synced 2025-02-17 06:08:26 +00:00
docs: improve components and elements
This commit is contained in:
parent
5b882ac409
commit
bdf234d728
9 changed files with 267 additions and 57 deletions
2
docs/guide/.vscode/spellright.dict
vendored
Normal file
2
docs/guide/.vscode/spellright.dict
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
oninput
|
||||
Webview
|
|
@ -5,12 +5,13 @@
|
|||
- [Hello, World!](hello_world.md)
|
||||
- [Describing the UI](elements/index.md)
|
||||
- [Intro to Elements](elements/vnodes.md)
|
||||
- [Intro to Components](elements/components.md)
|
||||
- [The Props Macro](elements/propsmacro.md)
|
||||
- [Reusing, Importing, and Exporting Components](elements/exporting_components.md)
|
||||
- [Passing children and attributes](elements/component_children.md)
|
||||
- [Conditional Rendering](elements/conditional_rendering.md)
|
||||
- [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)
|
||||
- [Adding Interactivity](interactivity/index.md)
|
||||
- [Hooks and Internal State](interactivity/hooks.md)
|
||||
- [Event handlers](interactivity/event_handlers.md)
|
||||
|
|
|
@ -156,42 +156,6 @@ For users of React: Dioxus knows *not* to memoize components that borrow propert
|
|||
|
||||
This means that during the render process, a newer version of `TitleCardProps` will never be compared with a previous version, saving some clock cycles.
|
||||
|
||||
## The inline_props macro
|
||||
|
||||
Yes - *another* macro! However, this one is entirely optional.
|
||||
|
||||
For internal components, we provide the `inline_props` macro, which will let you embed your `Props` definition right into the function arguments of your component.
|
||||
|
||||
Our title card above would be transformed from:
|
||||
|
||||
```rust
|
||||
#[derive(Props, PartialEq)]
|
||||
struct TitleCardProps {
|
||||
title: String,
|
||||
}
|
||||
|
||||
fn TitleCard(cx: Scope<TitleCardProps>) -> Element {
|
||||
cx.render(rsx!{
|
||||
h1 { "{cx.props.title}" }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```rust
|
||||
#[inline_props]
|
||||
fn TitleCard(cx: Scope, title: String) -> Element {
|
||||
cx.render(rsx!{
|
||||
h1 { "{title}" }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Again, this macro is optional and should not be used by library authors since you have less fine-grained control over documentation and optionality.
|
||||
|
||||
However, it's great for quickly throwing together an app without dealing with *any* extra boilerplate.
|
||||
|
||||
## The `Scope` object
|
||||
|
||||
Though very similar to React, Dioxus is different in a few ways. Most notably, React components will not have a `Scope` parameter in the component declaration.
|
||||
|
|
|
@ -75,19 +75,16 @@ Next, we're going to define our component:
|
|||
|
||||
```rust
|
||||
fn App(cx: Scope<PostList>) -> Element {
|
||||
// First, we create a new iterator by mapping the post array
|
||||
let posts = cx.props.posts.iter().map(|post| rsx!{
|
||||
Post {
|
||||
title: post.title,
|
||||
age: post.age,
|
||||
original_poster: post.original_poster
|
||||
}
|
||||
});
|
||||
|
||||
// Finally, we render the post list inside of a container
|
||||
cx.render(rsx!{
|
||||
ul { class: "post-list"
|
||||
{posts}
|
||||
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
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1 +1,47 @@
|
|||
# The Props Macro
|
||||
# Component Properties
|
||||
|
||||
All component `properties` must implement the `Properties` trait. The `Props` macro automatically derives this trait but adds some additional functionality. In this section, we'll learn about:
|
||||
|
||||
- Using the props macro
|
||||
- Memoization through PartialEq
|
||||
- Optional fields on props
|
||||
- The inline_props macro
|
||||
|
||||
|
||||
|
||||
|
||||
## The inline_props macro
|
||||
|
||||
Yes - *another* macro! However, this one is entirely optional.
|
||||
|
||||
For internal components, we provide the `inline_props` macro, which will let you embed your `Props` definition right into the function arguments of your component.
|
||||
|
||||
Our title card above would be transformed from:
|
||||
|
||||
```rust
|
||||
#[derive(Props, PartialEq)]
|
||||
struct TitleCardProps {
|
||||
title: String,
|
||||
}
|
||||
|
||||
fn TitleCard(cx: Scope<TitleCardProps>) -> Element {
|
||||
cx.render(rsx!{
|
||||
h1 { "{cx.props.title}" }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```rust
|
||||
#[inline_props]
|
||||
fn TitleCard(cx: Scope, title: String) -> Element {
|
||||
cx.render(rsx!{
|
||||
h1 { "{title}" }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Again, this macro is optional and should not be used by library authors since you have less fine-grained control over documentation and optionality.
|
||||
|
||||
However, it's great for quickly throwing together an app without dealing with *any* extra boilerplate.
|
||||
|
|
181
docs/guide/src/elements/special_attributes.md
Normal file
181
docs/guide/src/elements/special_attributes.md
Normal file
|
@ -0,0 +1,181 @@
|
|||
# Special Attributes
|
||||
|
||||
Dioxus tries its hardest to stay close to React, but there are some divergences and "special behavior" that you should review before moving on.
|
||||
|
||||
In this section, we'll cover special attributes built into Dioxus:
|
||||
|
||||
- `dangerous_inner_html`
|
||||
- Boolean attributes
|
||||
- `prevent_default`
|
||||
- `..Attributes`
|
||||
- event handlers as string attributes
|
||||
- `value`, `checked`, and `selected`
|
||||
|
||||
## The HTML escape hatch: `dangerous_inner_html`
|
||||
|
||||
One thing you might've missed from React is the ability to render raw HTML directly to the DOM. If you're working with pre-rendered assets, output from templates, or output from a JS library, then you might want to pass HTML directly instead of going through Dioxus. In these instances, reach for `dangerous_inner_html`.
|
||||
|
||||
For example, shipping a markdown-to-Dioxus converter might significantly bloat your final application size. Instead, you'll want to pre-render your markdown to HTML and then include the HTML directly in your output. We use this approach for the `http://dioxuslabs.com` site:
|
||||
|
||||
|
||||
```rust
|
||||
fn BlogPost(cx: Scope) -> Element {
|
||||
let contents = include_str!("../post.html");
|
||||
cx.render(rsx!{
|
||||
div {
|
||||
class: "markdown",
|
||||
dangerous_inner_html: "{contents}",
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
> 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
|
||||
|
||||
Most attributes, when rendered, will be rendered exactly as the input you provided. However, some attributes are considered "boolean" attributes and just their presence determines whether or not they affect the output. For these attributes, a provided value of `"false"` will cause them to be removed from the target element.
|
||||
|
||||
So the input of:
|
||||
|
||||
```rust
|
||||
rsx!{
|
||||
div {
|
||||
hidden: "false",
|
||||
"hello"
|
||||
}
|
||||
}
|
||||
```
|
||||
would actually render an output of
|
||||
```html
|
||||
<div>hello</div>
|
||||
```
|
||||
|
||||
Notice how `hidden` is not present in the final output?
|
||||
|
||||
Not all attributes work like this however. Only *these specific attributes* are whitelisted to have this behavior:
|
||||
|
||||
- `allowfullscreen`
|
||||
- `allowpaymentrequest`
|
||||
- `async`
|
||||
- `autofocus`
|
||||
- `autoplay`
|
||||
- `checked`
|
||||
- `controls`
|
||||
- `default`
|
||||
- `defer`
|
||||
- `disabled`
|
||||
- `formnovalidate`
|
||||
- `hidden`
|
||||
- `ismap`
|
||||
- `itemscope`
|
||||
- `loop`
|
||||
- `multiple`
|
||||
- `muted`
|
||||
- `nomodule`
|
||||
- `novalidate`
|
||||
- `open`
|
||||
- `playsinline`
|
||||
- `readonly`
|
||||
- `required`
|
||||
- `reversed`
|
||||
- `selected`
|
||||
- `truespeed`
|
||||
|
||||
For any other attributes, a value of `"false"` will be sent directly to the DOM.
|
||||
|
||||
## `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.
|
||||
|
||||
> 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.
|
||||
|
||||
```rust
|
||||
rsx!{
|
||||
input {
|
||||
oninput: move |_| {},
|
||||
prevent_default: "oninput",
|
||||
|
||||
onclick: move |_| {},
|
||||
prevent_default: "onclick",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `..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.
|
||||
|
||||
|
||||
```rust
|
||||
#[derive(Props)]
|
||||
pub struct InputProps<'a> {
|
||||
pub children: Element<'a>,
|
||||
pub attributes: Attribute<'a>
|
||||
}
|
||||
|
||||
pub fn StateInput<'a>(cx: Scope<'a, InputProps<'a>>) -> Element {
|
||||
cx.render(rsx! (
|
||||
input {
|
||||
..cx.props.attributes,
|
||||
&cx.props.children,
|
||||
}
|
||||
))
|
||||
}
|
||||
```
|
||||
|
||||
## 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:
|
||||
```rust
|
||||
let value = use_state(&cx, || String::from("hello world"));
|
||||
|
||||
rsx! {
|
||||
input {
|
||||
oninput: move |evt| value.set(evt.value.clone()),
|
||||
value: "{value}",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
With uncontrolled inputs, we won't actually drive the value from the component. This has its advantages when we don't want to re-render the component when the user inputs a value. We could either select the element directly - something Dioxus doesn't support across platforms - or we could handle `oninput` and modify a value without causing an update:
|
||||
|
||||
```rust
|
||||
let value = use_ref(&cx, || String::from("hello world"));
|
||||
|
||||
rsx! {
|
||||
input {
|
||||
oninput: move |evt| *value.write_silent() = evt.value.clone(),
|
||||
// no "value" is driven
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Strings for handlers like `onclick`
|
||||
|
||||
For element fields that take a handler like `onclick` or `oninput`, Dioxus will let you attach a closure. Alternatively, you can also pass a string using normal attribute syntax and assign this attribute on the DOM.
|
||||
|
||||
This lets you escape into JavaScript (only if your renderer can execute JavaScript).
|
||||
|
||||
```rust
|
||||
rsx!{
|
||||
div {
|
||||
// handle oninput with rust
|
||||
oninput: move |_| {},
|
||||
|
||||
// or handle oninput with javascript
|
||||
oninput: "alert('hello world')",
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Wrapping up
|
|
@ -81,13 +81,13 @@ let name = "Bob";
|
|||
rsx! ( "hello {name}" )
|
||||
```
|
||||
|
||||
Unfortunately, you cannot drop in arbitrary expressions directly into the string literal. In the cases where we need to compute a complex value, we'll want to use `format_args!` directly. Due to specifics of how the `rsx!` macro (we'll cover later), our call to `format_args` must be contained within curly braces *and* square braces.
|
||||
Unfortunately, you cannot drop in arbitrary expressions directly into the string literal. In the cases where we need to compute a complex value, we'll want to use `format_args!` directly. Due to specifics of how the `rsx!` macro (we'll cover later), our call to `format_args` must be contained within square braces.
|
||||
|
||||
```rust
|
||||
rsx!( {[format_args!("Hello {}", if enabled { "Jack" } else { "Bob" } )]} )
|
||||
rsx!( {format_args!("Hello {}", if enabled { "Jack" } else { "Bob" } )] )
|
||||
```
|
||||
|
||||
Alternatively, `&str` can be included directly, though it must be inside of an array:
|
||||
Alternatively, `&str` can be included directly, though it must be inside of square braces:
|
||||
|
||||
```rust
|
||||
rsx!( "Hello ", [if enabled { "Jack" } else { "Bob" }] )
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
# User Input and Controlled Components
|
||||
|
||||
Handling user input is one of the most common things your app will do, but it can be tricky
|
||||
|
|
|
@ -4,5 +4,22 @@ Every app you'll build with Dioxus will have some sort of state that needs to be
|
|||
|
||||
In this chapter, we'll cover the various ways to manage state, the appropriate terminology, various patterns, and then take an overview
|
||||
|
||||
|
||||
## Terminology
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Important hook: `use_state`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Important hook: `use_ref`
|
||||
|
||||
|
||||
|
||||
|
||||
## `provide_context` and `consume_context`
|
||||
|
|
Loading…
Add table
Reference in a new issue