mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-26 06:00:21 +00:00
Document props and component macro (#2522)
* document prop attributes * document children props * fix children doc test
This commit is contained in:
parent
f042e0029c
commit
3a4860add4
8 changed files with 861 additions and 229 deletions
323
packages/core-macro/docs/component.md
Normal file
323
packages/core-macro/docs/component.md
Normal file
|
@ -0,0 +1,323 @@
|
|||
# Component
|
||||
|
||||
The component macro turns a function with arguments that are [`Clone`] and [`PartialEq`] into a component. This is the recommended way of creating most components. If you want more fine grained control over how the overall prop struct implements the `Properties` trait, you can use an explicit props struct with the [`Props`] derive macro instead.
|
||||
|
||||
## Arguments
|
||||
|
||||
- `no_case_check` - Doesn't enforce `PascalCase` on your component names.
|
||||
**This will be removed/deprecated in a future update in favor of a more complete Clippy-backed linting system.**
|
||||
The reasoning behind this is that Clippy allows more robust and powerful lints, whereas
|
||||
macros are extremely limited.
|
||||
|
||||
## Features
|
||||
|
||||
This attribute:
|
||||
|
||||
- Enforces that your component uses `PascalCase` or `snake_case` with at least one underscore.
|
||||
- Automatically creates a prop struct for your component if the function has arguments.
|
||||
- Verifies the function signature is valid as a component.
|
||||
|
||||
## Examples
|
||||
|
||||
- Without props:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn GreetBob() -> Element {
|
||||
rsx! { "hello, bob" }
|
||||
}
|
||||
```
|
||||
|
||||
- With props:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn GreetBob(bob: String) -> Element {
|
||||
rsx! { "hello, {bob}" }
|
||||
}
|
||||
```
|
||||
|
||||
## Prop Modifiers
|
||||
|
||||
You can use the `#[props()]` attribute to modify the behavior of the props the component macro creates:
|
||||
|
||||
- [`#[props(default)]`](#default-props) - Makes the field optional in the component and uses the default value if it is not set when creating the component.
|
||||
- [`#[props(!optional)]`](#optional-props) - Makes a field with the type `Option<T>` required.
|
||||
- [`#[props(into)]`](#converting-props) - Converts a field into the correct type by using the [`Into`] trait.
|
||||
- [`#[props(extends = GlobalAttributes)]`](#extending-elements) - Extends the props with all the attributes from an element or the global element attributes.
|
||||
|
||||
Props also act slightly differently when used with:
|
||||
|
||||
- [`Option<T>`](#optional-props) - The field is automatically optional with a default value of `None`.
|
||||
- [`ReadOnlySignal<T>`](#reactive-props) - The props macro will automatically convert `T` into `ReadOnlySignal<T>` when it is passed as a prop.
|
||||
- [`String`](#formatted-props) - The props macro will accept formatted strings for any prop field with the type `String`.
|
||||
- [`children`](#children-props) - The props macro will accept child elements if you include the `children` prop.
|
||||
|
||||
### Default Props
|
||||
|
||||
The `default` attribute lets you define a default value for a field if it isn't set when creating the component
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn Button(
|
||||
// The default attributes makes your field optional in the component and uses the default value if it is not set.
|
||||
#[props(default)]
|
||||
text: String,
|
||||
// You can also set an explicit default value instead of using the `Default` implementation.
|
||||
#[props(default = "red".to_string())]
|
||||
color: String,
|
||||
) -> Element {
|
||||
rsx! {
|
||||
button {
|
||||
color: color,
|
||||
"{text}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rsx! {
|
||||
// You can skip setting props that have a default value when you use the component.
|
||||
Button {}
|
||||
};
|
||||
```
|
||||
|
||||
### Optional Props
|
||||
|
||||
When defining a component, you may want to make a prop optional without defining an explicit default value. Any fields with the type `Option<T>` are automatically optional with a default value of `None`.
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn Button(
|
||||
// Since the `text` field is optional, you don't need to set it when you use the component.
|
||||
text: Option<String>,
|
||||
) -> Element {
|
||||
rsx! {
|
||||
button { {text.unwrap_or("button".to_string())} }
|
||||
}
|
||||
}
|
||||
|
||||
rsx! {
|
||||
Button {}
|
||||
};
|
||||
```
|
||||
|
||||
If you want to make your `Option<T>` field required, you can use the `!optional` attribute:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn Button(
|
||||
// You can use the `!optional` attribute on a field with the type `Option<T>` to make it required.
|
||||
#[props(!optional)]
|
||||
text: Option<String>,
|
||||
) -> Element {
|
||||
rsx! {
|
||||
button { {text.unwrap_or("button".to_string())} }
|
||||
}
|
||||
}
|
||||
|
||||
rsx! {
|
||||
Button {
|
||||
text: None
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Converting Props
|
||||
|
||||
You can automatically convert a field into the correct type by using the `into` attribute. Any type you pass into the field will be converted with the [`Into`] trait:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn Button(
|
||||
// You can use the `into` attribute on a field to convert types you pass in with the Into trait.
|
||||
#[props(into)]
|
||||
number: u64,
|
||||
) -> Element {
|
||||
rsx! {
|
||||
button { "{number}" }
|
||||
}
|
||||
}
|
||||
|
||||
rsx! {
|
||||
Button {
|
||||
// Because we used the into attribute, we can pass in any type that implements Into<u64>
|
||||
number: 10u8
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Formatted Props
|
||||
|
||||
You can use formatted strings in attributes just like you would in an element. Any prop field with the type `String` can accept a formatted string:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn Button(text: String,) -> Element {
|
||||
rsx! {
|
||||
button { "{text}" }
|
||||
}
|
||||
}
|
||||
|
||||
let name = "Bob";
|
||||
rsx! {
|
||||
Button {
|
||||
// You can use formatted strings in props that accept String just like you would in an element.
|
||||
text: "Hello {name}!"
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Children Props
|
||||
|
||||
Rather than passing the RSX through a regular prop, you may wish to accept children similarly to how elements can have children. The "magic" children prop lets you achieve this:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn Clickable(
|
||||
href: String,
|
||||
children: Element,
|
||||
) -> Element {
|
||||
rsx! {
|
||||
a {
|
||||
href: "{href}",
|
||||
class: "fancy-button",
|
||||
{children}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This makes providing children to the component much simpler: simply put the RSX inside the {} brackets:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
# #[component]
|
||||
# fn Clickable(
|
||||
# href: String,
|
||||
# children: Element,
|
||||
# ) -> Element {
|
||||
# rsx! {
|
||||
# a {
|
||||
# href: "{href}",
|
||||
# class: "fancy-button",
|
||||
# {children}
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
rsx! {
|
||||
Clickable {
|
||||
href: "https://www.youtube.com/watch?v=C-M2hs3sXGo",
|
||||
"How to "
|
||||
i { "not" }
|
||||
" be seen"
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Reactive Props
|
||||
|
||||
In dioxus, when a prop changes, the component will rerun with the new value to update the UI. For example, if count changes from 0 to 1, this component will rerun and update the UI to show "Count: 1":
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn Counter(count: i32) -> Element {
|
||||
rsx! {
|
||||
div {
|
||||
"Count: {count}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Generally, just rerunning the component is enough to update the UI. However, if you use your prop inside reactive hooks like `use_memo` or `use_resource`, you may also want to restart those hooks when the prop changes:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn Counter(count: i32) -> Element {
|
||||
// We can use a memo to calculate the doubled count. Since this memo will only be created the first time the component is run and `count` is not reactive, it will never update when `count` changes.
|
||||
let doubled_count = use_memo(move || count * 2);
|
||||
rsx! {
|
||||
div {
|
||||
"Count: {count}"
|
||||
"Doubled Count: {doubled_count}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To fix this issue you can either:
|
||||
|
||||
1. Make the prop reactive by wrapping it in `ReadOnlySignal` (recommended):
|
||||
|
||||
`ReadOnlySignal` is a `Copy` reactive value. Dioxus will automatically convert any value into a `ReadOnlySignal` when it is passed as a prop.
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn Counter(count: ReadOnlySignal<i32>) -> Element {
|
||||
// Since we made count reactive, the memo will automatically rerun when count changes.
|
||||
let doubled_count = use_memo(move || count() * 2);
|
||||
rsx! {
|
||||
div {
|
||||
"Count: {count}"
|
||||
"Doubled Count: {doubled_count}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Explicitly add the prop as a dependency to the reactive hook with [`use_reactive`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/macro.use_reactive.html):
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn Counter(count: i32) -> Element {
|
||||
// We can add the count prop as an explicit dependency to every reactive hook that uses it with use_reactive.
|
||||
// The use_reactive macro takes a closure with explicit dependencies as its argument.
|
||||
let doubled_count = use_memo(use_reactive!(|count| count * 2));
|
||||
rsx! {
|
||||
div {
|
||||
"Count: {count}"
|
||||
"Doubled Count: {doubled_count}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Extending Elements
|
||||
|
||||
The `extends` attribute lets you extend your props with all the attributes from an element or the global element attributes.
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn Button(
|
||||
// You can use the `extends` attribute on a field with the type `Vec<Attribute>` to extend the props with all the attributes from an element or the global element attributes.
|
||||
#[props(extends = GlobalAttributes)]
|
||||
attributes: Vec<Attribute>,
|
||||
) -> Element {
|
||||
rsx! {
|
||||
// Instead of copying over every single attribute, we can just spread the attributes from the props into the button.
|
||||
button { ..attributes, "button" }
|
||||
}
|
||||
}
|
||||
|
||||
rsx! {
|
||||
// Since we extend global attributes, you can use any attribute that would normally appear on the button element.
|
||||
Button {
|
||||
width: "10px",
|
||||
height: "10px",
|
||||
color: "red",
|
||||
}
|
||||
};
|
||||
```
|
339
packages/core-macro/docs/props.md
Normal file
339
packages/core-macro/docs/props.md
Normal file
|
@ -0,0 +1,339 @@
|
|||
# Props
|
||||
|
||||
The props derive macro allows you to define what props your component accepts and how to accept those props. Every component must either accept no arguments or accept a single argument that implements the `Properties` trait.
|
||||
|
||||
> Note: You should generally prefer using the `#[component]` macro instead of the `#[derive(Props)]` macro with explicit props. The `#[component]` macro will automatically generate the props struct for you and perform extra checks to ensure that your component is valid.
|
||||
|
||||
## Example
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[derive(Props, PartialEq, Clone)]
|
||||
struct ButtonProps {
|
||||
/// The text of the button
|
||||
text: String,
|
||||
|
||||
/// The color of the button
|
||||
color: String,
|
||||
}
|
||||
|
||||
fn Button(props: ButtonProps) -> Element {
|
||||
rsx! {
|
||||
button {
|
||||
color: props.color,
|
||||
"{props.text}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rsx! {
|
||||
// Any fields you defined on the props struct will be turned into props for the component.
|
||||
Button {
|
||||
text: "Click me!",
|
||||
color: "red",
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Prop Modifiers
|
||||
|
||||
You can use the `#[props()]` attribute to modify the behavior of the props derive macro:
|
||||
|
||||
- [`#[props(default)]`](#default-props) - Makes the field optional in the component and uses the default value if it is not set when creating the component.
|
||||
- [`#[props(!optional)]`](#optional-props) - Makes a field with the type `Option<T>` required.
|
||||
- [`#[props(into)]`](#converting-props) - Converts a field into the correct type by using the [`Into`] trait.
|
||||
- [`#[props(extends = GlobalAttributes)]`](#extending-elements) - Extends the props with all the attributes from an element or the global element attributes.
|
||||
|
||||
Props also act slightly differently when used with:
|
||||
|
||||
- [`Option<T>`](#optional-props) - The field is automatically optional with a default value of `None`.
|
||||
- [`ReadOnlySignal<T>`](#reactive-props) - The props macro will automatically convert `T` into `ReadOnlySignal<T>` when it is passed as a prop.
|
||||
- [`String`](#formatted-props) - The props macro will accept formatted strings for any prop field with the type `String`.
|
||||
- [`children`](#children-props) - The props macro will accept child elements if you include the `children` prop.
|
||||
|
||||
### Default Props
|
||||
|
||||
The `default` attribute lets you define a default value for a field if it isn't set when creating the component
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[derive(Props, PartialEq, Clone)]
|
||||
struct ButtonProps {
|
||||
// The default attributes makes your field optional in the component and uses the default value if it is not set.
|
||||
#[props(default)]
|
||||
text: String,
|
||||
|
||||
/// You can also set an explicit default value instead of using the `Default` implementation.
|
||||
#[props(default = "red".to_string())]
|
||||
color: String,
|
||||
}
|
||||
|
||||
fn Button(props: ButtonProps) -> Element {
|
||||
rsx! {
|
||||
button {
|
||||
color: props.color,
|
||||
"{props.text}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rsx! {
|
||||
// You can skip setting props that have a default value when you use the component.
|
||||
Button {}
|
||||
};
|
||||
```
|
||||
|
||||
### Optional Props
|
||||
|
||||
When defining props, you may want to make a prop optional without defining an explicit default value. Any fields with the type `Option<T>` are automatically optional with a default value of `None`.
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[derive(Props, PartialEq, Clone)]
|
||||
struct ButtonProps {
|
||||
// Since the `text` field is optional, you don't need to set it when you use the component.
|
||||
text: Option<String>,
|
||||
}
|
||||
|
||||
fn Button(props: ButtonProps) -> Element {
|
||||
rsx! {
|
||||
button { {props.text.unwrap_or("button".to_string())} }
|
||||
}
|
||||
}
|
||||
|
||||
rsx! {
|
||||
Button {}
|
||||
};
|
||||
```
|
||||
|
||||
If you want to make your `Option<T>` field required, you can use the `!optional` attribute:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[derive(Props, PartialEq, Clone)]
|
||||
struct ButtonProps {
|
||||
/// You can use the `!optional` attribute on a field with the type `Option<T>` to make it required.
|
||||
#[props(!optional)]
|
||||
text: Option<String>,
|
||||
}
|
||||
|
||||
fn Button(props: ButtonProps) -> Element {
|
||||
rsx! {
|
||||
button { {props.text.unwrap_or("button".to_string())} }
|
||||
}
|
||||
}
|
||||
|
||||
rsx! {
|
||||
Button {
|
||||
text: None
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Converting Props
|
||||
|
||||
You can automatically convert a field into the correct type by using the `into` attribute. Any type you pass into the field will be converted with the [`Into`] trait:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[derive(Props, PartialEq, Clone)]
|
||||
struct ButtonProps {
|
||||
/// You can use the `into` attribute on a field to convert types you pass in with the Into trait.
|
||||
#[props(into)]
|
||||
number: u64,
|
||||
}
|
||||
|
||||
fn Button(props: ButtonProps) -> Element {
|
||||
rsx! {
|
||||
button { "{props.number}" }
|
||||
}
|
||||
}
|
||||
|
||||
rsx! {
|
||||
Button {
|
||||
// Because we used the into attribute, we can pass in any type that implements Into<u64>
|
||||
number: 10u8
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Formatted Props
|
||||
|
||||
You can use formatted strings in attributes just like you would in an element. Any prop field with the type `String` can accept a formatted string:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[derive(Props, PartialEq, Clone)]
|
||||
struct ButtonProps {
|
||||
text: String,
|
||||
}
|
||||
|
||||
fn Button(props: ButtonProps) -> Element {
|
||||
rsx! {
|
||||
button { "{props.text}" }
|
||||
}
|
||||
}
|
||||
|
||||
let name = "Bob";
|
||||
rsx! {
|
||||
Button {
|
||||
// You can use formatted strings in props that accept String just like you would in an element.
|
||||
text: "Hello {name}!"
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Children Props
|
||||
|
||||
Rather than passing the RSX through a regular prop, you may wish to accept children similarly to how elements can have children. The "magic" children prop lets you achieve this:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[derive(PartialEq, Clone, Props)]
|
||||
struct ClickableProps {
|
||||
href: String,
|
||||
children: Element,
|
||||
}
|
||||
|
||||
fn Clickable(props: ClickableProps) -> Element {
|
||||
rsx! {
|
||||
a {
|
||||
href: "{props.href}",
|
||||
class: "fancy-button",
|
||||
{props.children}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This makes providing children to the component much simpler: simply put the RSX inside the {} brackets:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
# #[derive(PartialEq, Clone, Props)]
|
||||
# struct ClickableProps {
|
||||
# href: String,
|
||||
# children: Element,
|
||||
# }
|
||||
#
|
||||
# fn Clickable(props: ClickableProps) -> Element {
|
||||
# rsx! {
|
||||
# a {
|
||||
# href: "{props.href}",
|
||||
# class: "fancy-button",
|
||||
# {props.children}
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
rsx! {
|
||||
Clickable {
|
||||
href: "https://www.youtube.com/watch?v=C-M2hs3sXGo",
|
||||
"How to "
|
||||
i { "not" }
|
||||
" be seen"
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Reactive Props
|
||||
|
||||
In dioxus, when a prop changes, the component will rerun with the new value to update the UI. For example, if count changes from 0 to 1, this component will rerun and update the UI to show "Count: 1":
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn Counter(count: i32) -> Element {
|
||||
rsx! {
|
||||
div {
|
||||
"Count: {count}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Generally, just rerunning the component is enough to update the UI. However, if you use your prop inside reactive hooks like `use_memo` or `use_resource`, you may also want to restart those hooks when the prop changes:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn Counter(count: i32) -> Element {
|
||||
// We can use a memo to calculate the doubled count. Since this memo will only be created the first time the component is run and `count` is not reactive, it will never update when `count` changes.
|
||||
let doubled_count = use_memo(move || count * 2);
|
||||
rsx! {
|
||||
div {
|
||||
"Count: {count}"
|
||||
"Doubled Count: {doubled_count}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To fix this issue you can either:
|
||||
|
||||
1. Make the prop reactive by wrapping it in `ReadOnlySignal` (recommended):
|
||||
|
||||
`ReadOnlySignal` is a `Copy` reactive value. Dioxus will automatically convert any value into a `ReadOnlySignal` when it is passed as a prop.
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn Counter(count: ReadOnlySignal<i32>) -> Element {
|
||||
// Since we made count reactive, the memo will automatically rerun when count changes.
|
||||
let doubled_count = use_memo(move || count() * 2);
|
||||
rsx! {
|
||||
div {
|
||||
"Count: {count}"
|
||||
"Doubled Count: {doubled_count}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Explicitly add the prop as a dependency to the reactive hook with [`use_reactive`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/macro.use_reactive.html):
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn Counter(count: i32) -> Element {
|
||||
// We can add the count prop as an explicit dependency to every reactive hook that uses it with use_reactive.
|
||||
// The use_reactive macro takes a closure with explicit dependencies as its argument.
|
||||
let doubled_count = use_memo(use_reactive!(|count| count * 2));
|
||||
rsx! {
|
||||
div {
|
||||
"Count: {count}"
|
||||
"Doubled Count: {doubled_count}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Extending Elements
|
||||
|
||||
The `extends` attribute lets you extend your props with all the attributes from an element or the global element attributes.
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[derive(Props, PartialEq, Clone)]
|
||||
struct ButtonProps {
|
||||
/// You can use the `extends` attribute on a field with the type `Vec<Attribute>` to extend the props with all the attributes from an element or the global element attributes.
|
||||
#[props(extends = GlobalAttributes)]
|
||||
attributes: Vec<Attribute>,
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Button(props: ButtonProps) -> Element {
|
||||
rsx! {
|
||||
// Instead of copying over every single attribute, we can just spread the attributes from the props into the button.
|
||||
button { ..props.attributes, "button" }
|
||||
}
|
||||
}
|
||||
|
||||
rsx! {
|
||||
// Since we extend global attributes, you can use any attribute that would normally appear on the button element.
|
||||
Button {
|
||||
width: "10px",
|
||||
height: "10px",
|
||||
color: "red",
|
||||
}
|
||||
};
|
||||
```
|
185
packages/core-macro/docs/rsx.md
Normal file
185
packages/core-macro/docs/rsx.md
Normal file
|
@ -0,0 +1,185 @@
|
|||
The rsx! macro makes it easy for developers to write jsx-style markup in their components.
|
||||
|
||||
## Elements
|
||||
|
||||
You can render elements with rsx! with the element name and then braces surrounding the attributes and children.
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
rsx! {
|
||||
div {
|
||||
div {}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Web Components</summary>
|
||||
|
||||
Dioxus will automatically render any elements with `-` as a untyped web component:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
rsx! {
|
||||
div-component {
|
||||
div {}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
You can wrap your web component in a custom component to add type checking:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn MyDivComponent(width: i64) -> Element {
|
||||
rsx! {
|
||||
div-component {
|
||||
"width": width
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Attributes
|
||||
|
||||
You can add attributes to any element inside the braces. Attributes are key-value pairs separated by a colon.
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
let width = 100;
|
||||
rsx! {
|
||||
div {
|
||||
// Set the class attribute to "my-class"
|
||||
class: "my-class",
|
||||
// attribute strings are automatically formatted with the format macro
|
||||
width: "{width}px",
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Optional Attributes
|
||||
|
||||
You can include optional attributes with an unterminated if statement as the value of the attribute:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
# let first_boolean = true;
|
||||
# let second_boolean = false;
|
||||
rsx! {
|
||||
div {
|
||||
// Set the class attribute to "my-class" if true
|
||||
class: if first_boolean {
|
||||
"my-class"
|
||||
},
|
||||
// Set the class attribute to "my-other-class" if false
|
||||
class: if second_boolean {
|
||||
"my-other-class"
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Raw Attributes
|
||||
|
||||
Dioxus defaults to attributes that are type checked as html. If you want to include an attribute that is not included in the html spec, you can use the `raw` attribute surrounded by quotes:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
rsx! {
|
||||
div {
|
||||
// Set the data-count attribute to "1"
|
||||
"data-count": "1"
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Text
|
||||
|
||||
You can include text in your markup as a string literal:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
let name = "World";
|
||||
rsx! {
|
||||
div {
|
||||
"Hello World"
|
||||
// Just like attributes, you can included formatted segments inside your text
|
||||
"Hello {name}"
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
You can render any [`macro@crate::component`]s you created inside your markup just like elements. Components must either start with a capital letter or contain a `_` character.
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
#[component]
|
||||
fn HelloWorld() -> Element {
|
||||
rsx! { "hello world!" }
|
||||
}
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
HelloWorld {}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## If statements
|
||||
|
||||
You can use if statements to conditionally render children. The body of the for if statement is parsed as rsx markup:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
let first_boolean = true;
|
||||
let second_boolean = false;
|
||||
rsx! {
|
||||
if first_boolean {
|
||||
div {
|
||||
"first"
|
||||
}
|
||||
}
|
||||
|
||||
if second_boolean {
|
||||
"second"
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## For loops
|
||||
|
||||
You can also use for loops to iterate over a collection of items. The body of the for loop is parsed as rsx markup:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
let numbers = vec![1, 2, 3];
|
||||
rsx! {
|
||||
for number in numbers {
|
||||
div {
|
||||
"{number}"
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Raw Expressions
|
||||
|
||||
You can include raw expressions inside your markup inside curly braces. Your expression must implement the [`IntoDynNode`](https://docs.rs/dioxus-core/latest/dioxus_core/trait.IntoDynNode.html) trait:
|
||||
|
||||
```rust, no_run
|
||||
# use dioxus::prelude::*;
|
||||
let name = "World";
|
||||
rsx! {
|
||||
div {
|
||||
// Text can be converted into a dynamic node in rsx
|
||||
{name}
|
||||
}
|
||||
// Iterators can also be converted into dynamic nodes
|
||||
{(0..10).map(|n| n * n).map(|number| rsx! { div { "{number}" } })}
|
||||
};
|
||||
```
|
|
@ -13,6 +13,13 @@ mod utils;
|
|||
|
||||
use dioxus_rsx as rsx;
|
||||
|
||||
/// Format a string with inline rust expressions. [`format_args_f!`] is very similar to [`format_args`], but it allows you to use arbitrary rust expressions inside braces instead of just variables:
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// let formatted_with_variables = format_args!("{} + {} = {}", 1, 2, 1 + 2);
|
||||
/// let formatted_with_inline_expressions = format_args_f!("{1} + {2} = {1 + 2}");
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn format_args_f(input: TokenStream) -> TokenStream {
|
||||
use rsx::*;
|
||||
|
@ -21,6 +28,7 @@ pub fn format_args_f(input: TokenStream) -> TokenStream {
|
|||
.into()
|
||||
}
|
||||
|
||||
#[doc = include_str!("../docs/props.md")]
|
||||
#[proc_macro_derive(Props, attributes(props))]
|
||||
pub fn derive_typed_builder(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as syn::DeriveInput);
|
||||
|
@ -30,192 +38,7 @@ pub fn derive_typed_builder(input: TokenStream) -> TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
/// The rsx! macro makes it easy for developers to write jsx-style markup in their components.
|
||||
///
|
||||
/// ## Elements
|
||||
///
|
||||
/// You can render elements with rsx! with the element name and then braces surrounding the attributes and children.
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// rsx! {
|
||||
/// div {
|
||||
/// div {}
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Web Components</summary>
|
||||
///
|
||||
///
|
||||
/// Dioxus will automatically render any elements with `-` as a untyped web component:
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// rsx! {
|
||||
/// div-component {
|
||||
/// div {}
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// You can wrap your web component in a custom component to add type checking:
|
||||
/// ```rust, no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// #[component]
|
||||
/// fn MyDivComponent(width: i64) -> Element {
|
||||
/// rsx! {
|
||||
/// div-component {
|
||||
/// "width": width
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// </details>
|
||||
///
|
||||
/// ## Attributes
|
||||
///
|
||||
/// You can add attributes to any element inside the braces. Attributes are key-value pairs separated by a colon.
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// let width = 100;
|
||||
/// rsx! {
|
||||
/// div {
|
||||
/// // Set the class attribute to "my-class"
|
||||
/// class: "my-class",
|
||||
/// // attribute strings are automatically formatted with the format macro
|
||||
/// width: "{width}px",
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// ### Optional Attributes
|
||||
///
|
||||
/// You can include optional attributes with an unterminated if statement as the value of the attribute:
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// # let first_boolean = true;
|
||||
/// # let second_boolean = false;
|
||||
/// rsx! {
|
||||
/// div {
|
||||
/// // Set the class attribute to "my-class" if true
|
||||
/// class: if first_boolean {
|
||||
/// "my-class"
|
||||
/// },
|
||||
/// // Set the class attribute to "my-other-class" if false
|
||||
/// class: if second_boolean {
|
||||
/// "my-other-class"
|
||||
/// }
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// ### Raw Attributes
|
||||
///
|
||||
/// Dioxus defaults to attributes that are type checked as html. If you want to include an attribute that is not included in the html spec, you can use the `raw` attribute surrounded by quotes:
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// rsx! {
|
||||
/// div {
|
||||
/// // Set the data-count attribute to "1"
|
||||
/// "data-count": "1"
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// ## Text
|
||||
///
|
||||
/// You can include text in your markup as a string literal:
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// let name = "World";
|
||||
/// rsx! {
|
||||
/// div {
|
||||
/// "Hello World"
|
||||
/// // Just like attributes, you can included formatted segments inside your text
|
||||
/// "Hello {name}"
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// ## Components
|
||||
///
|
||||
/// You can render any [`macro@crate::component`]s you created inside your markup just like elements. Components must either start with a capital letter or contain a `_` character.
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// #[component]
|
||||
/// fn HelloWorld() -> Element {
|
||||
/// rsx! { "hello world!" }
|
||||
/// }
|
||||
///
|
||||
/// rsx! {
|
||||
/// div {
|
||||
/// HelloWorld {}
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// ## If statements
|
||||
///
|
||||
/// You can use if statements to conditionally render children. The body of the for if statement is parsed as rsx markup:
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// let first_boolean = true;
|
||||
/// let second_boolean = false;
|
||||
/// rsx! {
|
||||
/// if first_boolean {
|
||||
/// div {
|
||||
/// "first"
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// if second_boolean {
|
||||
/// "second"
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// ## For loops
|
||||
///
|
||||
/// You can also use for loops to iterate over a collection of items. The body of the for loop is parsed as rsx markup:
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// let numbers = vec![1, 2, 3];
|
||||
/// rsx! {
|
||||
/// for number in numbers {
|
||||
/// div {
|
||||
/// "{number}"
|
||||
/// }
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// ## Raw Expressions
|
||||
///
|
||||
/// You can include raw expressions inside your markup inside curly braces. Your expression must implement the [`IntoDynNode`](https://docs.rs/dioxus-core/latest/dioxus_core/trait.IntoDynNode.html) trait:
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// let name = "World";
|
||||
/// rsx! {
|
||||
/// div {
|
||||
/// // Text can be converted into a dynamic node in rsx
|
||||
/// {name}
|
||||
/// }
|
||||
/// // Iterators can also be converted into dynamic nodes
|
||||
/// {(0..10).map(|n| n * n).map(|number| rsx! { div { "{number}" } })}
|
||||
/// };
|
||||
/// ```
|
||||
#[doc = include_str!("../docs/rsx.md")]
|
||||
#[proc_macro]
|
||||
pub fn rsx(tokens: TokenStream) -> TokenStream {
|
||||
match syn::parse::<rsx::CallBody>(tokens) {
|
||||
|
@ -231,45 +54,7 @@ pub fn render(tokens: TokenStream) -> TokenStream {
|
|||
rsx(tokens)
|
||||
}
|
||||
|
||||
/// Streamlines component creation.
|
||||
/// This is the recommended way of creating components,
|
||||
/// though you might want lower-level control with more advanced uses.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `no_case_check` - Doesn't enforce `PascalCase` on your component names.
|
||||
/// **This will be removed/deprecated in a future update in favor of a more complete Clippy-backed linting system.**
|
||||
/// The reasoning behind this is that Clippy allows more robust and powerful lints, whereas
|
||||
/// macros are extremely limited.
|
||||
///
|
||||
/// # Features
|
||||
/// This attribute:
|
||||
/// * Enforces that your component uses `PascalCase`.
|
||||
/// No warnings are generated for the `PascalCase`
|
||||
/// function name, but everything else will still raise a warning if it's incorrectly `PascalCase`.
|
||||
/// Does not disable warnings anywhere else, so if you, for example,
|
||||
/// accidentally don't use `snake_case`
|
||||
/// for a variable name in the function, the compiler will still warn you.
|
||||
/// * Automatically uses `#[inline_props]` if there's more than 1 parameter in the function.
|
||||
/// * Verifies the validity of your component.
|
||||
///
|
||||
/// # Examples
|
||||
/// * Without props:
|
||||
/// ```rust, no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// #[component]
|
||||
/// fn GreetBob() -> Element {
|
||||
/// rsx! { "hello, bob" }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// * With props:
|
||||
/// ```rust, no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// #[component]
|
||||
/// fn GreetBob(bob: String) -> Element {
|
||||
/// rsx! { "hello, {bob}" }
|
||||
/// }
|
||||
/// ```
|
||||
#[doc = include_str!("../docs/component.md")]
|
||||
#[proc_macro_attribute]
|
||||
pub fn component(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
parse_macro_input!(input as ComponentBody)
|
||||
|
|
|
@ -270,7 +270,7 @@ pub fn schedule_update_any() -> Arc<dyn Fn(ScopeId) + Send + Sync> {
|
|||
|
||||
/// Creates a callback that will be run before the component is removed.
|
||||
/// This can be used to clean up side effects from the component
|
||||
/// (created with [`use_effect`](crate::use_effect)).
|
||||
/// (created with [`use_effect`](dioxus::prelude::use_effect)).
|
||||
///
|
||||
/// Example:
|
||||
/// ```rust
|
||||
|
|
|
@ -438,7 +438,7 @@ impl ScopeId {
|
|||
|
||||
/// Create a subscription that schedules a future render for the reference component. Unlike [`Self::needs_update`], this function will work outside of the dioxus runtime.
|
||||
///
|
||||
/// ## Notice: you should prefer using [`schedule_update_any`]
|
||||
/// ## Notice: you should prefer using [`crate::prelude::schedule_update_any`]
|
||||
pub fn schedule_update(&self) -> Arc<dyn Fn() + Send + Sync + 'static> {
|
||||
Runtime::with_scope(*self, |cx| cx.schedule_update()).expect("to be in a dioxus runtime")
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
//! - `router`: exports the [router](https://dioxuslabs.com/learn/0.5/router) and enables any router features for the current platform
|
||||
//! - `third-party-renderer`: Just disables warnings about no active platform when no renderers are enabled
|
||||
//!
|
||||
//! Platform features (the current platform determines what platform the [`launch`](dioxus::prelude::launch) function runs):
|
||||
//! Platform features (the current platform determines what platform the [`launch()`] function runs):
|
||||
//!
|
||||
//! - `fullstack`: enables the fullstack platform. This must be used in combination with the `web` feature for wasm builds and `axum` feature for server builds
|
||||
//! - `desktop`: enables the desktop platform
|
||||
|
|
|
@ -42,4 +42,4 @@
|
|||
//! /// }
|
||||
//! ///
|
||||
//! /// We only track this when the template changes
|
||||
//! pub discovered_templates: Vec<Template>,
|
||||
//! pub discovered_templates: Vec<crate::Template>,
|
||||
|
|
Loading…
Reference in a new issue