mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
355 lines
9.8 KiB
Plaintext
355 lines
9.8 KiB
Plaintext
---
|
|
title: 'Properties'
|
|
description: 'Parent to child communication'
|
|
---
|
|
|
|
import Tabs from '@theme/Tabs'
|
|
import TabItem from '@theme/TabItem'
|
|
|
|
:::note
|
|
|
|
Properties are often shortened as "Props".
|
|
|
|
:::
|
|
|
|
Properties are essentially component arguments that Yew can keep watch on.
|
|
|
|
A type has to implement the `Properties` trait before it can be used as the properties of a component.
|
|
|
|
## Reactivity
|
|
|
|
Yew checks if props have changed when reconciling the Virtual DOM during re-rendering, to know if nested components need to be re-rendered.
|
|
This way Yew can be considered a very reactive framework, as changes from the parent will always be propagated downward,
|
|
and the view will never be out of sync with the data coming from props/state.
|
|
|
|
:::tip
|
|
|
|
If you have not yet completed the [tutorial](../../tutorial), try it out and test this reactivity yourself!
|
|
|
|
:::
|
|
|
|
## Derive macro
|
|
|
|
Yew provides a derive macro to easily implement the `Properties` trait on structs.
|
|
|
|
Types for which you derive `Properties` must also implement `PartialEq` so Yew can do data comparison.
|
|
|
|
```rust
|
|
use yew::Properties;
|
|
|
|
#[derive(Properties, PartialEq)]
|
|
pub struct Props {
|
|
pub is_loading: bool,
|
|
}
|
|
```
|
|
|
|
## Use in function components
|
|
|
|
The attribute `#[function_component]` allows to optionally receive Props in the function arguments. To supply them,
|
|
they are assigned via attributes in the `html!` macro.
|
|
|
|
<Tabs>
|
|
<TabItem value="with-props" label="With Props">
|
|
|
|
```rust
|
|
use yew::{function_component, html, Html, Properties};
|
|
|
|
#[derive(Properties, PartialEq)]
|
|
pub struct Props {
|
|
pub is_loading: bool,
|
|
}
|
|
|
|
#[function_component]
|
|
fn HelloWorld(&Props { is_loading }: &Props) -> Html {
|
|
html! { <>{"Am I loading? - "}{is_loading}</> }
|
|
}
|
|
|
|
// Then supply the prop
|
|
#[function_component]
|
|
fn App() -> Html {
|
|
html! { <HelloWorld is_loading=true /> }
|
|
}
|
|
|
|
```
|
|
|
|
</TabItem>
|
|
<TabItem value="no-props" label="No Props">
|
|
|
|
```rust
|
|
use yew::{function_component, html, Html};
|
|
|
|
#[function_component]
|
|
fn HelloWorld() -> Html {
|
|
html! { "Hello world" }
|
|
}
|
|
|
|
// No props to supply
|
|
#[function_component]
|
|
fn App() -> Html {
|
|
html! { <HelloWorld /> }
|
|
}
|
|
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
## Derive macro field attributes
|
|
|
|
When deriving `Properties` all fields are required by default.
|
|
The following attributes allow you to give your props default values which will be used when the parent has not set them.
|
|
|
|
:::tip
|
|
Attributes aren't visible in Rustdoc generated documentation.
|
|
The doc strings of your properties should mention whether a prop is optional and if it has a special default value.
|
|
:::
|
|
|
|
<Tabs>
|
|
<TabItem value="prop_or_default" label="#[prop_or_default]">
|
|
|
|
Initialize the prop value with the default value of the field's type using the `Default` trait.
|
|
|
|
```rust
|
|
use yew::{function_component, html, Html, Properties};
|
|
|
|
#[derive(Properties, PartialEq)]
|
|
pub struct Props {
|
|
// highlight-start
|
|
#[prop_or_default]
|
|
// highlight-end
|
|
pub is_loading: bool,
|
|
}
|
|
|
|
#[function_component]
|
|
fn HelloWorld(&Props { is_loading }: &Props) -> Html {
|
|
if is_loading {
|
|
html! { "Loading" }
|
|
} else {
|
|
html! { "Hello world" }
|
|
}
|
|
}
|
|
|
|
// Then use like this with default
|
|
#[function_component]
|
|
fn Case1() -> Html {
|
|
html! { <HelloWorld /> }
|
|
}
|
|
// Or no override the default
|
|
#[function_component]
|
|
fn Case2() -> Html {
|
|
html! { <HelloWorld is_loading=true /> }
|
|
}
|
|
```
|
|
|
|
</TabItem>
|
|
<TabItem value="prop_or_value" label="#[prop_or(value)]">
|
|
|
|
Use `value` to initialize the prop value. `value` can be any expression that returns the field's type.
|
|
For example, to default a boolean prop to `true`, use the attribute `#[prop_or(true)]`. The expression
|
|
is evaluated when the properties are constructed and no explicit value has been given.
|
|
|
|
```rust
|
|
use yew::prelude::*;
|
|
|
|
#[derive(Properties, PartialEq)]
|
|
pub struct Props {
|
|
#[prop_or_default]
|
|
pub is_loading: bool,
|
|
// highlight-start
|
|
#[prop_or(AttrValue::Static("Bob"))]
|
|
// highlight-end
|
|
pub name: AttrValue,
|
|
}
|
|
|
|
#[function_component]
|
|
fn Hello(&Props { is_loading, ref name }: &Props) -> Html {
|
|
if is_loading {
|
|
html! { "Loading" }
|
|
} else {
|
|
html! { <>{"Hello "}{name} </>}
|
|
}
|
|
}
|
|
|
|
// Then use like this with default
|
|
#[function_component]
|
|
fn Case1() -> Html {
|
|
html! { <Hello /> }
|
|
}
|
|
// Or no override the default
|
|
#[function_component]
|
|
fn Case2() -> Html {
|
|
html! { <Hello name="Sam" /> }
|
|
}
|
|
```
|
|
|
|
</TabItem>
|
|
<TabItem value="prop_or_else_function" label="#[prop_or_else(function)]">
|
|
|
|
Call `function` to initialize the prop value. `function` should have the signature `FnMut() -> T` where `T` is the field type.
|
|
The function is called when no explicit value has been given for that attribute.
|
|
|
|
```rust
|
|
use yew::prelude::*;
|
|
|
|
fn create_default_name() -> AttrValue {
|
|
AttrValue::Static("Bob")
|
|
}
|
|
|
|
#[derive(Properties, PartialEq)]
|
|
pub struct Props {
|
|
#[prop_or_default]
|
|
pub is_loading: bool,
|
|
// highlight-start
|
|
#[prop_or_else(create_default_name)]
|
|
// highlight-end
|
|
pub name: AttrValue,
|
|
}
|
|
|
|
#[function_component]
|
|
fn Hello(&Props { is_loading, ref name }: &Props) -> Html {
|
|
if is_loading {
|
|
html! { "Loading" }
|
|
} else {
|
|
html! { <>{"Hello "}{name}</> }
|
|
}
|
|
}
|
|
|
|
// Then use like this with default
|
|
#[function_component]
|
|
fn Case1() -> Html {
|
|
html! { <Hello /> }
|
|
}
|
|
// Or no override the default
|
|
#[function_component]
|
|
fn Case2() -> Html {
|
|
html! { <Hello name="Sam" /> }
|
|
}
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
## Memory/speed overhead of using Properties
|
|
|
|
Internally properties are reference counted. This means that only a shared pointer is passed down the component tree for props.
|
|
It saves us from the cost of having to clone the entire props, which might be expensive.
|
|
|
|
:::tip
|
|
Make use of `AttrValue` which is our custom type for attribute values instead of defining them as String or another similar type.
|
|
:::
|
|
|
|
## Props macro
|
|
|
|
The `yew::props!` macro allows you to build properties the same way the `html!` macro does it.
|
|
|
|
The macro uses the same syntax as a struct expression except that you can't use attributes or a base expression (`Foo { ..base }`).
|
|
The type path can either point to the props directly (`path::to::Props`) or the associated properties of a component (`MyComp::Properties`).
|
|
|
|
```rust
|
|
use yew::prelude::*;
|
|
|
|
#[derive(Properties, PartialEq)]
|
|
pub struct Props {
|
|
#[prop_or_default]
|
|
pub is_loading: bool,
|
|
#[prop_or(AttrValue::Static("Bob"))]
|
|
pub name: AttrValue,
|
|
}
|
|
|
|
#[function_component]
|
|
fn Hello(&Props { is_loading, ref name }: &Props) -> Html {
|
|
if is_loading {
|
|
html! { "Loading" }
|
|
} else {
|
|
html! { <>{"Hello "}{name}</> }
|
|
}
|
|
}
|
|
|
|
#[function_component]
|
|
fn App() -> Html {
|
|
// highlight-start
|
|
let pre_made_props = yew::props! {
|
|
Props {} // Notice we did not need to specify name prop
|
|
};
|
|
// highlight-end
|
|
html! { <Hello ..pre_made_props /> }
|
|
}
|
|
```
|
|
|
|
## Automatically generate properties (yew-autoprops)
|
|
|
|
In order to streamline your development process, you can also use the macro
|
|
`#[autoprops]` (from the crate `yew-autoprops`) that will automatically
|
|
generate the `Properties` struct for you.
|
|
|
|
```rust
|
|
use yew::prelude::*;
|
|
use yew_autoprops::autoprops;
|
|
|
|
// the #[autoprops] macro must appear BEFORE #[function_component], the order matters
|
|
#[autoprops]
|
|
#[function_component]
|
|
fn Greetings(
|
|
#[prop_or_default]
|
|
is_loading: bool,
|
|
#[prop_or(AttrValue::Static("Hello"))]
|
|
message: &AttrValue,
|
|
#[prop_or(AttrValue::Static("World"))]
|
|
name: &AttrValue,
|
|
) -> Html {
|
|
if is_loading {
|
|
html! { "Loading" }
|
|
} else {
|
|
html! { <>{message}{" "}{name}</> }
|
|
}
|
|
}
|
|
|
|
// The properties struct "GreetingsProps" will be generated automatically.
|
|
//
|
|
// `is_loading` will be passed as value to the components while `message` and
|
|
// `name` will use references because of the leading `&` in the definition.
|
|
```
|
|
|
|
## Evaluation Order
|
|
|
|
Props are evaluated in the order they're specified, as shown by the following example:
|
|
|
|
```rust
|
|
#[derive(yew::Properties, PartialEq)]
|
|
struct Props { first: usize, second: usize, last: usize }
|
|
|
|
let mut g = 1..=3;
|
|
let props = yew::props!(Props { first: g.next().unwrap(), second: g.next().unwrap(), last: g.next().unwrap() });
|
|
|
|
assert_eq!(props.first, 1);
|
|
assert_eq!(props.second, 2);
|
|
assert_eq!(props.last, 3);
|
|
```
|
|
|
|
## Anti Patterns
|
|
|
|
While almost any Rust type can be passed as properties, there are some anti-patterns that should be avoided.
|
|
These include, but are not limited to:
|
|
|
|
1. Using `String` type instead of `AttrValue`. <br />
|
|
**Why is this bad?** `String` can be expensive to clone.
|
|
Cloning is often needed when the prop value is used with hooks and callbacks. `AttrValue` is either
|
|
a reference-counted string (`Rc<str>`) or a `&'static str`, thus very cheap to clone.<br />
|
|
**Note**: `AttrValue` internally is `IString` from [implicit-clone](https://crates.io/crates/implicit-clone)
|
|
See that crate to learn more.
|
|
2. Using interior mutability. <br />
|
|
**Why is this bad?** Interior mutability (such as with `RefCell`, `Mutex`, etc.) should
|
|
_generally_ be avoided. It can cause problems with re-renders (Yew doesn't know when the state has changed)
|
|
so you may have to manually force a render. Like all things, it has its place. Use it with caution.
|
|
3. Using `Vec` type instead of `IArray`. <br />
|
|
**Why is this bad?** `Vec`, just like `String`, can also be expensive to clone. `IArray` is either
|
|
a reference-counted slice (`Rc<T>`) or a `&'static [T]`, thus very cheap to clone.<br />
|
|
**Note**: `IArray` can be imported from [implicit-clone](https://crates.io/crates/implicit-clone)
|
|
See that crate to learn more.
|
|
4. You tell us. Did you run into an edge-case you wish you knew about earlier? Feel free to create an issue
|
|
or PR a fix to this documentation.
|
|
|
|
## yew-autoprops
|
|
|
|
[yew-autoprops](https://crates.io/crates/yew-autoprops) is an experimental package that allows one to create the Props struct on the fly out of the arguments of your function. Might be useful, if the properties struct is never reused.
|