Properties

Properties - 图1备注

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

Properties - 图2提示

If you have not yet completed the 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.

  1. use yew::Properties;
  2. #[derive(Properties, PartialEq)]
  3. pub struct Props {
  4. pub is_loading: bool,
  5. }

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.

  • With Props
  • No Props
  1. use yew::{function_component, html, Html, Properties};
  2. #[derive(Properties, PartialEq)]
  3. pub struct Props {
  4. pub is_loading: bool,
  5. }
  6. #[function_component]
  7. fn HelloWorld(props: &Props) -> Html {
  8. html! { <>{"Am I loading? - "}{props.is_loading.clone()}</> }
  9. }
  10. // Then supply the prop
  11. #[function_component]
  12. fn App() -> Html {
  13. html! {<HelloWorld is_loading={true} />}
  14. }
  1. use yew::{function_component, html, Html};
  2. #[function_component]
  3. fn HelloWorld() -> Html {
  4. html! { "Hello world" }
  5. }
  6. // No props to supply
  7. #[function_component]
  8. fn App() -> Html {
  9. html! {<HelloWorld />}
  10. }

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 parent has not set them.

Properties - 图3提示

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.

  • [prop_or_default]

  • [prop_or(value)]

  • [prop_or_else(function)]

Initialize the prop value with the default value of the field’s type using the Default trait.

  1. use yew::{function_component, html, Html, Properties};
  2. #[derive(Properties, PartialEq)]
  3. pub struct Props {
  4. #[prop_or_default]
  5. pub is_loading: bool,
  6. }
  7. #[function_component]
  8. fn HelloWorld(props: &Props) -> Html {
  9. if props.is_loading.clone() {
  10. html! { "Loading" }
  11. } else {
  12. html! { "Hello world" }
  13. }
  14. }
  15. // Then use like this with default
  16. #[function_component]
  17. fn Case1() -> Html {
  18. html! {<HelloWorld />}
  19. }
  20. // Or no override the default
  21. #[function_component]
  22. fn Case2() -> Html {
  23. html! {<HelloWorld is_loading={true} />}
  24. }

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.

  1. use yew::{function_component, html, Html, Properties};
  2. #[derive(Properties, PartialEq)]
  3. pub struct Props {
  4. #[prop_or("Bob".to_string())]
  5. pub name: String,
  6. }
  7. #[function_component]
  8. fn HelloWorld(props: &Props) -> Html {
  9. html! {<>{"Hello world"}{props.name.clone()}</>}
  10. }
  11. // Then use like this with default
  12. #[function_component]
  13. fn Case1() -> Html {
  14. html! {<HelloWorld />}
  15. }
  16. // Or no override the default
  17. #[function_component]
  18. fn Case2() -> Html {
  19. html! {<HelloWorld name={"Sam".to_string()} />}
  20. }

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.

  1. use yew::{function_component, html, Html, Properties};
  2. fn create_default_name() -> String {
  3. "Bob".to_string()
  4. }
  5. #[derive(Properties, PartialEq)]
  6. pub struct Props {
  7. #[prop_or_else(create_default_name)]
  8. pub name: String,
  9. }
  10. #[function_component]
  11. fn HelloWorld(props: &Props) -> Html {
  12. html! {<>{"Hello world"}{props.name.clone()}</>}
  13. }
  14. // Then use like this with default
  15. #[function_component]
  16. fn Case1() -> Html {
  17. html! {<HelloWorld />}
  18. }
  19. // Or no override the default
  20. #[function_component]
  21. fn Case2() -> Html {
  22. html! {<HelloWorld name={"Sam".to_string()} />}
  23. }

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.

Properties - 图4提示

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

  1. use yew::{function_component, html, Html, Properties, props, virtual_dom::AttrValue};
  2. #[derive(Properties, PartialEq)]
  3. pub struct Props {
  4. #[prop_or(AttrValue::from("Bob"))]
  5. pub name: AttrValue,
  6. }
  7. #[function_component]
  8. fn HelloWorld(props: &Props) -> Html {
  9. html! {<>{"Hello world"}{props.name.clone()}</>}
  10. }
  11. #[function_component]
  12. fn App() -> Html {
  13. let pre_made_props = props! {
  14. Props {} // Notice we did not need to specify name prop
  15. };
  16. html! {<HelloWorld ..pre_made_props />}
  17. }

Evaluation Order

Props are evaluated in the order they’re specified, as shown by the following example:

  1. #[derive(yew::Properties, PartialEq)]
  2. struct Props { first: usize, second: usize, last: usize }
  3. fn main() {
  4. let mut g = 1..=3;
  5. let props = yew::props!(Props { first: g.next().unwrap(), second: g.next().unwrap(), last: g.next().unwrap() });
  6. assert_eq!(props.first, 1);
  7. assert_eq!(props.second, 2);
  8. assert_eq!(props.last, 3);
  9. }

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.
    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 is IString from implicit-clone See that crate to learn more.
  2. Using interior mutability.
    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 state has changed) so you may have to manually force a render. Like all things, it has its place. Use it with caution.
  3. 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.