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>,
}
#[function_component(Filter)]
pub fn filter(props: &FilterProps) -> Html {
#[function_component]
pub fn Filter(props: &FilterProps) -> Html {
let filter = props.filter;
let cls = if props.selected {

View File

@ -1,5 +1,5 @@
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::punctuated::Punctuated;
use syn::spanned::Spanned;
@ -140,18 +140,22 @@ impl Parse for FunctionComponent {
}
pub struct FunctionComponentName {
component_name: Ident,
component_name: Option<Ident>,
}
impl Parse for FunctionComponentName {
fn parse(input: ParseStream) -> syn::Result<Self> {
if input.is_empty() {
return Err(input.error("expected identifier for the component"));
return Ok(Self {
component_name: None,
});
}
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,
return_type,
} = 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();
if function_name == component_name {

View File

@ -1,23 +1,25 @@
error: expected identifier
--> $DIR/bad-name-fail.rs:8:22
--> tests/function_component_attr/bad-name-fail.rs:8:22
|
8 | #[function_component(let)]
| ^^^
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)]
| ^
error: expected identifier
--> $DIR/bad-name-fail.rs:26:22
--> tests/function_component_attr/bad-name-fail.rs:26:22
|
26 | #[function_component(124)]
| ^^^
error: the component must not have the same name as the function
--> $DIR/bad-name-fail.rs:35:22
warning: type `component` should have an upper camel case name
--> tests/function_component_attr/bad-name-fail.rs:35:22
|
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> /> };
| ^^^^ 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
|
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
|
@ -36,7 +36,7 @@ error[E0599]: the function or associated item `new` exists for struct `VChild<Fu
| ----------------------------------------------------------- doesn't satisfy `_: yew::Component`
|
= 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`
--> tests/function_component_attr/generic-props-fail.rs:30:14

View File

@ -5,8 +5,8 @@ struct Props {
a: usize,
}
#[function_component()]
fn comp(props: &Props) -> Html {
#[function_component]
fn Comp(props: &Props) -> Html {
html! {
<p>
{ 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`).
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:
```rust
@ -42,8 +61,8 @@ pub struct RenderedAtProps {
pub time: String,
}
#[function_component(RenderedAt)]
pub fn rendered_at(props: &RenderedAtProps) -> Html {
#[function_component]
pub fn RenderedAt(props: &RenderedAtProps) -> Html {
html! {
<p>
<b>{ "Rendered at: " }</b>
@ -59,8 +78,8 @@ pub fn rendered_at(props: &RenderedAtProps) -> Html {
```rust
use yew::{function_component, html, use_state, Callback};
#[function_component(App)]
fn app() -> Html {
#[function_component]
fn App() -> Html {
let counter = use_state(|| 0);
let onclick = {
@ -99,8 +118,8 @@ where
data: T,
}
#[function_component(MyGenericComponent)]
pub fn my_generic_component<T>(props: &Props<T>) -> Html
#[function_component]
pub fn MyGenericComponent<T>(props: &Props<T>) -> Html
where
T: PartialEq + Display,
{

View File

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