Allow function_component creation based on function name (#2292)

* function_component without name

* fmt

* fc

* update docs

* remove fc
This commit is contained in:
Muhammad Hamza 2021-12-23 17:30:17 +05:00 committed by GitHub
parent 398f38d5fa
commit 73b5f6b4c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 63 additions and 36 deletions

View File

@ -8,8 +8,8 @@ pub struct FilterProps {
pub onset_filter: Callback<FilterEnum>, pub onset_filter: Callback<FilterEnum>,
} }
#[function_component(Filter)] #[function_component]
pub fn filter(props: &FilterProps) -> Html { pub fn Filter(props: &FilterProps) -> Html {
let filter = props.filter; let filter = props.filter;
let cls = if props.selected { let cls = if props.selected {

View File

@ -1,5 +1,5 @@
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::{quote, quote_spanned, ToTokens}; use quote::{format_ident, quote, quote_spanned, ToTokens};
use syn::parse::{Parse, ParseStream}; use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated; use syn::punctuated::Punctuated;
use syn::spanned::Spanned; use syn::spanned::Spanned;
@ -140,18 +140,22 @@ impl Parse for FunctionComponent {
} }
pub struct FunctionComponentName { pub struct FunctionComponentName {
component_name: Ident, component_name: Option<Ident>,
} }
impl Parse for FunctionComponentName { impl Parse for FunctionComponentName {
fn parse(input: ParseStream) -> syn::Result<Self> { fn parse(input: ParseStream) -> syn::Result<Self> {
if input.is_empty() { if input.is_empty() {
return Err(input.error("expected identifier for the component")); return Ok(Self {
component_name: None,
});
} }
let component_name = input.parse()?; let component_name = input.parse()?;
Ok(Self { component_name }) Ok(Self {
component_name: Some(component_name),
})
} }
} }
@ -171,7 +175,12 @@ pub fn function_component_impl(
name: function_name, name: function_name,
return_type, return_type,
} = component; } = component;
let component_name = component_name.unwrap_or_else(|| function_name.clone());
let function_name = format_ident!(
"{}FunctionProvider",
function_name,
span = function_name.span()
);
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
if function_name == component_name { if function_name == component_name {

View File

@ -1,23 +1,25 @@
error: expected identifier error: expected identifier
--> $DIR/bad-name-fail.rs:8:22 --> tests/function_component_attr/bad-name-fail.rs:8:22
| |
8 | #[function_component(let)] 8 | #[function_component(let)]
| ^^^ | ^^^
error: unexpected token error: unexpected token
--> $DIR/bad-name-fail.rs:17:23 --> tests/function_component_attr/bad-name-fail.rs:17:23
| |
17 | #[function_component(x, y, z)] 17 | #[function_component(x, y, z)]
| ^ | ^
error: expected identifier error: expected identifier
--> $DIR/bad-name-fail.rs:26:22 --> tests/function_component_attr/bad-name-fail.rs:26:22
| |
26 | #[function_component(124)] 26 | #[function_component(124)]
| ^^^ | ^^^
error: the component must not have the same name as the function warning: type `component` should have an upper camel case name
--> $DIR/bad-name-fail.rs:35:22 --> tests/function_component_attr/bad-name-fail.rs:35:22
| |
35 | #[function_component(component)] 35 | #[function_component(component)]
| ^^^^^^^^^ | ^^^^^^^^^ help: convert the identifier to upper camel case (notice the capitalization): `Component`
|
= note: `#[warn(non_camel_case_types)]` on by default

View File

@ -22,13 +22,13 @@ error[E0277]: the trait bound `MissingTypeBounds: yew::Properties` is not satisf
27 | html! { <Comp<MissingTypeBounds> /> }; 27 | html! { <Comp<MissingTypeBounds> /> };
| ^^^^ the trait `yew::Properties` is not implemented for `MissingTypeBounds` | ^^^^ the trait `yew::Properties` is not implemented for `MissingTypeBounds`
| |
= note: required because of the requirements on the impl of `FunctionProvider` for `comp<MissingTypeBounds>` = note: required because of the requirements on the impl of `FunctionProvider` for `compFunctionProvider<MissingTypeBounds>`
error[E0599]: the function or associated item `new` exists for struct `VChild<FunctionComponent<comp<MissingTypeBounds>>>`, but its trait bounds were not satisfied error[E0599]: the function or associated item `new` exists for struct `VChild<FunctionComponent<compFunctionProvider<MissingTypeBounds>>>`, but its trait bounds were not satisfied
--> tests/function_component_attr/generic-props-fail.rs:27:14 --> tests/function_component_attr/generic-props-fail.rs:27:14
| |
27 | html! { <Comp<MissingTypeBounds> /> }; 27 | html! { <Comp<MissingTypeBounds> /> };
| ^^^^ function or associated item cannot be called on `VChild<FunctionComponent<comp<MissingTypeBounds>>>` due to unsatisfied trait bounds | ^^^^ function or associated item cannot be called on `VChild<FunctionComponent<compFunctionProvider<MissingTypeBounds>>>` due to unsatisfied trait bounds
| |
::: $WORKSPACE/packages/yew/src/functional/mod.rs ::: $WORKSPACE/packages/yew/src/functional/mod.rs
| |
@ -36,7 +36,7 @@ error[E0599]: the function or associated item `new` exists for struct `VChild<Fu
| ----------------------------------------------------------- doesn't satisfy `_: yew::Component` | ----------------------------------------------------------- doesn't satisfy `_: yew::Component`
| |
= note: the following trait bounds were not satisfied: = note: the following trait bounds were not satisfied:
`FunctionComponent<comp<MissingTypeBounds>>: yew::Component` `FunctionComponent<compFunctionProvider<MissingTypeBounds>>: yew::Component`
error[E0107]: missing generics for type alias `Comp` error[E0107]: missing generics for type alias `Comp`
--> tests/function_component_attr/generic-props-fail.rs:30:14 --> tests/function_component_attr/generic-props-fail.rs:30:14

View File

@ -5,8 +5,8 @@ struct Props {
a: usize, a: usize,
} }
#[function_component()] #[function_component]
fn comp(props: &Props) -> Html { fn Comp(props: &Props) -> Html {
html! { html! {
<p> <p>
{ props.a } { props.a }
@ -14,4 +14,8 @@ fn comp(props: &Props) -> Html {
} }
} }
fn main() {} fn main() {
let _ = html! {
<Comp a={0} />
};
}

View File

@ -1,7 +0,0 @@
error: unexpected end of input, expected identifier for the component
--> $DIR/no-name-fail.rs:8:1
|
8 | #[function_component()]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)

View File

@ -11,7 +11,26 @@ Functions with the attribute have to return `Html` and may take a single paramet
The parameter type needs to be a reference to a `Properties` type (ex. `props: &MyProps`). The parameter type needs to be a reference to a `Properties` type (ex. `props: &MyProps`).
If the function doesn't have any parameters the resulting component doesn't accept any props. If the function doesn't have any parameters the resulting component doesn't accept any props.
The attribute doesn't replace your original function with a component. You need to provide a name as an input to the attribute which will be the identifier of the component. Just mark the component with the attribute. The component will be named after the function.
```rust
use yew::{function_component, html, Html};
#[function_component]
pub fn ChatContainer() -> Html {
html! {
// chat container impl
}
}
html! {
<ChatContainer />
};
```
## Specifying a custom component name
You need to provide a name as an input to the attribute which will be the identifier of the component.
Assuming you have a function called `chat_container` and you add the attribute `#[function_component(ChatContainer)]` you can use the component like this: Assuming you have a function called `chat_container` and you add the attribute `#[function_component(ChatContainer)]` you can use the component like this:
```rust ```rust
@ -42,8 +61,8 @@ pub struct RenderedAtProps {
pub time: String, pub time: String,
} }
#[function_component(RenderedAt)] #[function_component]
pub fn rendered_at(props: &RenderedAtProps) -> Html { pub fn RenderedAt(props: &RenderedAtProps) -> Html {
html! { html! {
<p> <p>
<b>{ "Rendered at: " }</b> <b>{ "Rendered at: " }</b>
@ -59,8 +78,8 @@ pub fn rendered_at(props: &RenderedAtProps) -> Html {
```rust ```rust
use yew::{function_component, html, use_state, Callback}; use yew::{function_component, html, use_state, Callback};
#[function_component(App)] #[function_component]
fn app() -> Html { fn App() -> Html {
let counter = use_state(|| 0); let counter = use_state(|| 0);
let onclick = { let onclick = {
@ -99,8 +118,8 @@ where
data: T, data: T,
} }
#[function_component(MyGenericComponent)] #[function_component]
pub fn my_generic_component<T>(props: &Props<T>) -> Html pub fn MyGenericComponent<T>(props: &Props<T>) -> Html
where where
T: PartialEq + Display, T: PartialEq + Display,
{ {

View File

@ -18,8 +18,8 @@ The easiest way to create a function component is to add the [`#[function_compon
```rust ```rust
use yew::{function_component, html}; use yew::{function_component, html};
#[function_component(HelloWorld)] #[function_component]
fn hello_world() -> Html { fn HelloWorld() -> Html {
html! { "Hello world" } html! { "Hello world" }
} }
``` ```