Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port yew-autoprops to yew-macro #3505

Merged
merged 22 commits into from
Dec 22, 2023
Merged
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 81 additions & 26 deletions website/docs/concepts/function-components/properties.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,14 @@ pub struct Props {
}

#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! { <>{"Am I loading? - "}{props.is_loading.clone()}</> }
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} />}
html! { <HelloWorld is_loading=true /> }
}

```
Expand All @@ -91,7 +91,7 @@ fn HelloWorld() -> Html {
// No props to supply
#[function_component]
fn App() -> Html {
html! {<HelloWorld />}
html! { <HelloWorld /> }
}

```
Expand Down Expand Up @@ -126,8 +126,8 @@ pub struct Props {
}

#[function_component]
fn HelloWorld(props: &Props) -> Html {
if props.is_loading.clone() {
fn HelloWorld(&Props { is_loading }: &Props) -> Html {
if is_loading {
html! { "Loading" }
} else {
html! { "Hello world" }
Expand All @@ -137,12 +137,12 @@ fn HelloWorld(props: &Props) -> Html {
// Then use like this with default
#[function_component]
fn Case1() -> Html {
html! {<HelloWorld />}
html! { <HelloWorld /> }
}
// Or no override the default
#[function_component]
fn Case2() -> Html {
html! {<HelloWorld is_loading={true} />}
html! { <HelloWorld is_loading=true /> }
}
```

Expand All @@ -158,26 +158,32 @@ use yew::{function_component, html, Html, Properties};

#[derive(Properties, PartialEq)]
pub struct Props {
#[prop_or_default]
pub is_loading: bool,
// highlight-start
#[prop_or("Bob".to_string())]
#[prop_or(AttrValue::Static("Bob"))]
// highlight-end
pub name: String,
pub name: AttrValue,
}

#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! {<>{"Hello world"}{props.name.clone()}</>}
fn Hello(&Props { is_loading, ref name }: &Props) -> Html {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using the syntax proposed here: yewstack/implicit-clone#39 (comment) by Alex Parrill. I think this should appear a lot more in the doc. (Please let me know your opinion on this.)

I think I learned that syntax in the context of match at the beginning I was using Rust but then I completely forgot about it and I'm not sure I have ever saw it used in destructuring like that.

Most of the time you will want to use this syntax. Actually probably always. Because it allows dereferencing the primitive types automatically (the Copy-able types). For types that are Implicit-lyCloneable like AttrValue it's actually best to take them by reference because you can pass that reference directly to the properties of child component. And it won't move it because it's a reference.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's certainly handy to know, but not to always use, since writing the name of the props type twice can get cumbersome really fast. If only Rust had something like anonymous struct patterns...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

YESSS I know..... well at least autoprops makes it easier in both ways (Prop struct creation + destructuring)

if is_loading {
html! { "Loading" }
} else {
html! { <>{"Hello "}{name} </>}
}
}

// Then use like this with default
#[function_component]
fn Case1() -> Html {
html! {<HelloWorld />}
html! { <Hello /> }
}
// Or no override the default
#[function_component]
fn Case2() -> Html {
html! {<HelloWorld name={"Sam".to_string()} />}
html! { <Hello name="Sam" /> }
}
```

Expand All @@ -190,32 +196,38 @@ The function is called when no explicit value has been given for that attribute.
```rust
use yew::{function_component, html, Html, Properties};

fn create_default_name() -> String {
"Bob".to_string()
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: String,
pub name: AttrValue,
}

#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! {<>{"Hello world"}{props.name.clone()}</>}
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! {<HelloWorld />}
html! { <Hello /> }
}
// Or no override the default
#[function_component]
fn Case2() -> Html {
html! {<HelloWorld name={"Sam".to_string()} />}
html! { <Hello name="Sam" /> }
}
```

Expand Down Expand Up @@ -243,13 +255,19 @@ use yew::{function_component, html, Html, Properties, props, virtual_dom::AttrVa

#[derive(Properties, PartialEq)]
pub struct Props {
#[prop_or(AttrValue::from("Bob"))]
#[prop_or_default]
pub is_loading: bool,
#[prop_or(AttrValue::Static("Bob"))]
pub name: AttrValue,
}

#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! {<>{"Hello world"}{props.name.clone()}</>}
fn Hello(&Props { name }: &Props) -> Html {
if is_loading {
html! { "Loading" }
} else {
html! { <>{"Hello "}{name}</> }
}
}

#[function_component]
Expand All @@ -259,10 +277,42 @@ fn App() -> Html {
Props {} // Notice we did not need to specify name prop
};
// highlight-end
html! {<HelloWorld ..pre_made_props />}
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::*;

#[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:
Expand Down Expand Up @@ -296,7 +346,12 @@ These include, but are not limited to:
**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. You tell us. Did you run into an edge-case you wish you knew about earlier? Feel free to create an issue
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
Expand Down
Loading