title | description |
---|---|
Properties |
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.
Yew checks if props have changed when reconciling the vdom during rerendering, to know if nested components needs to be rerendered. This way Yew can be considered a very reactive framework as changes from the parent will always be propagated downwards and the view will never be out of sync from the data coming from props/state.
:::tip
If you have not yet completed the tutorial, try it out and test this reactivity yourself!
:::
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.
use yew::Properties;
#[derive(Properties, PartialEq)]
pub struct Props {
pub is_loading: bool,
}
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.
use yew::{function_component, html, Html, Properties};
#[derive(Properties, PartialEq)]
pub struct Props {
pub is_loading: bool,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! { <>{"Am I loading? - "}{props.is_loading.clone()}</> }
}
// Then supply the prop
#[function_component]
fn App() -> Html {
html! {<HelloWorld is_loading={true} />}
}
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 />}
}
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 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. :::
Initialize the prop value with the default value of the field's type using the Default
trait.
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: &Props) -> Html {
if props.is_loading.clone() {
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} />}
}
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.
use yew::{function_component, html, Html, Properties};
#[derive(Properties, PartialEq)]
pub struct Props {
// highlight-start
#[prop_or("Bob".to_string())]
// highlight-end
pub name: String,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! {<>{"Hello world"}{props.name.clone()}</>}
}
// 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 name={"Sam".to_string()} />}
}
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.
use yew::{function_component, html, Html, Properties};
fn create_default_name() -> String {
"Bob".to_string()
}
#[derive(Properties, PartialEq)]
pub struct Props {
// highlight-start
#[prop_or_else(create_default_name)]
// highlight-end
pub name: String,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! {<>{"Hello world"}{props.name.clone()}</>}
}
// 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 name={"Sam".to_string()} />}
}
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.
:::
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
).
use yew::{function_component, html, Html, Properties, props, virtual_dom::AttrValue};
#[derive(Properties, PartialEq)]
pub struct Props {
#[prop_or(AttrValue::from("Bob"))]
pub name: AttrValue,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! {<>{"Hello world"}{props.name.clone()}</>}
}
#[function_component]
fn App() -> Html {
// highlight-start
let pre_made_props = props! {
Props {} // Notice we did not need to specify name prop
};
// highlight-end
html! {<HelloWorld ..pre_made_props />}
}
Props are evaluated in the order they're specified, as shown by the following example:
#[derive(yew::Properties, PartialEq)]
struct Props { first: usize, second: usize, last: usize }
fn main() {
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);
}
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:
- Using
String
type instead ofAttrValue
.
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.
Note:AttrValue
internally isIString
from implicit-clone See that crate to learn more. - Using interior mutability.
Why is this bad? Interior mutability (such as withRefCell
,Mutex
, etc.) should generally be avoided. It can cause problems with re-renders (Yew doesn't know when state has changed) so you may have to manually force a render. Like all things, it has its place. Use it with caution. - 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.