Convert components concept docs from SC to FC (#2434)

* Convert components concept docs from SC to FC
- moved struct components to advanced topics
- added docs about HOC for Suspense and Context
- added a ease-in topic before components that introduces
HTML/CSS/JS in yew
- edit components concept to use function components

* translations

* fix todo links

* fix tests

* spelling bee
This commit is contained in:
Julius Lungys 2022-02-07 11:03:12 +02:00 committed by GitHub
parent bd373d2861
commit 636692507e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 1648 additions and 712 deletions

View File

@ -1,12 +1,18 @@
//! Function components are a simplified version of normal components.
//! They consist of a single function annotated with the attribute `#[function_component(_)]`
//! They consist of a single function annotated with the attribute `#[function_component]`
//! that receives props and determines what should be rendered by returning [`Html`](crate::Html).
//!
//! Functions with the attribute have to return `Html` and may take a single parameter for the type of props the component should accept.
//! 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.
//!
//! Just mark the component with the attribute. The component will be named after the function.
//!
//! ```rust
//! # use yew::prelude::*;
//! #
//! #[function_component(HelloWorld)]
//! fn hello_world() -> Html {
//! #[function_component]
//! fn HelloWorld() -> Html {
//! html! { "Hello world" }
//! }
//! ```

View File

@ -0,0 +1,82 @@
---
title: "Higher Order Components"
---
There are several cases where Struct components dont directly support a feature (ex. Suspense) or require a lot of boiler plate to use the features (ex. Context).
In those cases it is recommended to create function components that are higher order components.
## Higher Order Components Definition
Higher Order Components are components that dont add any new Html and only wrap some other component to provide extra functionality.
### Example
Hook into Context and pass it down to a struct component
```rust
use yew::prelude::*;
#[derive(Clone, Debug, PartialEq)]
struct Theme {
foreground: String,
background: String,
}
#[function_component]
pub fn App() -> Html {
let ctx = use_state(|| Theme {
foreground: "#000000".to_owned(),
background: "#eeeeee".to_owned(),
});
html! {
<ContextProvider<Theme> context={(*ctx).clone()}>
<ThemedButtonHOC />
</ContextProvider<Theme>>
}
}
// highlight-start
#[function_component]
pub fn ThemedButtonHOC() -> Html {
let theme = use_context::<Theme>().expect("no ctx found");
html! {<ThemedButtonStructComponent {theme} />}
}
// highlight-end
#[derive(Properties, PartialEq)]
pub struct Props {
pub theme: Theme,
}
struct ThemedButtonStructComponent;
impl Component for ThemedButtonStructComponent {
type Message = ();
type Properties = Props;
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, ctx: &Context<Self>) -> Html {
let theme = &ctx.props().theme;
html! {
<button style={format!(
"background: {}; color: {};",
theme.background,
theme.foreground
)}
>
{ "Click me!" }
</button>
}
}
}
```

View File

@ -1,7 +1,6 @@
---
title: "Introduction"
description: "Components in Yew"
slug: /concepts/components
---
## What are Components?
@ -21,7 +20,7 @@ much stricter. It also provides super-powers like conditional rendering and rend
## Passing data to a component
Yew components use *props* to communicate between parent and children. A parent component may pass any data as props to
Yew components use _props_ to communicate between parent and children. A parent component may pass any data as props to
its children. Props are similar to HTML attributes but any Rust type can be passed as props.
:::info

View File

@ -21,7 +21,7 @@ The following attributes allow you to give your props initial values which will
:::tip
Attributes aren't visible in Rustdoc generated documentation.
The docstrings of your properties should mention whether a prop is optional and if it has a special default value.
The doc strings of your properties should mention whether a prop is optional and if it has a special default value.
:::
#### `#[prop_or_default]`

View File

@ -0,0 +1,104 @@
---
title: "CSS with classes!"
description: "A handy macro to handle classes"
comment: "Keep this file as short and simple as possible. Its purpose is to ease in the reader into components in Yew instead of providing proper API docs"
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
> Yew mostly operates on the idea of keeping everything that a reusable piece of
> UI may need, in one place - rust files. But also seeks to stay close to the
> original look of the technology.
Yew does not provide css in rs solutions natively, but helps with css by providing
programmatic ways to interact with css classes.
## Classes
The struct `Classes` simplifies use of HTML classes:
<Tabs>
<TabItem value="Literal" label="Literal">
```rust
use yew::{classes, html};
html! {
<div class={classes!("container")}></div>
};
```
</TabItem>
<TabItem value="Multiple" label="Multiple">
```rust
use yew::{classes, html};
html! {
<div class={classes!("class-1", "class-2")}></div>
};
```
</TabItem>
<TabItem value="String" label="String">
```rust
use yew::{classes, html};
html! {
<div class={classes!(String::from("class-1 class-2"))}></div>
};
```
</TabItem>
<TabItem value="Optional" label="Optional">
```rust
use yew::{classes, html};
html! {
<div class={classes!(Some("class"))} />
};
```
</TabItem>
<TabItem value="Vector" label="Vector">
```rust
use yew::{classes, html};
html! {
<div class={classes!(vec!["class-1", "class-2"])}></div>
};
```
</TabItem>
<TabItem value="Slice" label="Slice">
```rust
use yew::{classes, html};
html! {
<div class={classes!(["class-1", "class-2"].as_ref())}></div>
};
```
</TabItem>
</Tabs>
We will expand upon this concept in [more CSS](../../more/css).
## Inline Styles
Currently Yew does not provide any help with inline styles natively:
```rust
use yew::{classes, html};
html! {
<div styles="color: red;"></div>
};
```
We will expand upon this concept in [more CSS](../../more/css).

View File

@ -0,0 +1,88 @@
---
title: "HTML with html!"
description: "Its HTML but not quite!"
comment: "Keep this file as short and simple as possible. Its purpose is to ease in the reader into components in Yew instead of providing proper API docs"
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
> Yew mostly operates on the idea of keeping everything that a reusable piece of
> UI may need, in one place - rust files. But also seeks to stay close to the
> original look of the technology.
For HTML we achieve this by providing the `html!` macro:
```rust
use yew::prelude::*;
let my_header: Html = html!{<img src="img_girl.jpg" alt="Girl in a jacket" width="500" height="600" />};
```
`html!` macro allows you to write html generation as if it was in a `.html` file and then behind the scenes by yew gets turned into internal builder patterns that generate all DOM nodes.
As it is part of rust code it provides a easy way to switch from html to rust by applying curly brackets:
```rust
use yew::prelude::*;
let header_text = "Hello world".to_string();
let header_html: Html = html!{<h1>{header_text}</h1>};
let count: usize = 5;
let counter_html: Html = html!{<p>{"My age is: "}{count}</p>};
let combined_html: Html = html!{<div>{header_html}{counter_html}</div>};
```
One rule major rule comes with use of `html!` - you can only return 1 wrapping node:
<Tabs>
<TabItem value="Valid" label="Valid">
```rust
use yew::html;
html! {
<div>
<div></div>
<p></p>
</div>
};
```
</TabItem>
<TabItem value="Invalid" label="Invalid">
```rust, compile_fail
use yew::html;
// error: only one root html element allowed
html! {
<div></div>
<p></p>
};
```
</TabItem>
</Tabs>
To help with this rule and avoid `div` abuse `html!` allows fragments. Fragments are empty tags that do not provide any html result:
```rust
use yew::html;
html! {
<>
<div></div>
<p></p>
</>
};
```
We will introduce Yew and HTML further in depth in [more HTML](../html).

View File

@ -0,0 +1,56 @@
---
title: "JS with RS"
description: "Javascript with Rust"
comment: "Keep this file as short and simple as possible. Its purpose is to ease in the reader into components in Yew instead of providing proper API docs"
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
> Yew mostly operates on the idea of keeping everything that a reusable piece of
> UI may need, in one place - rust files. But also seeks to stay close to the
> original look of the technology.
Sadly as of today WebAssembly is not feature-complete for DOM interactions. This means even in Yew we sometimes rely on calling Javascript.
## wasm-bindgen
[`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) is a library and tool that allows to call javascript from rust and rust from javascript.
We highly recommend you give a look to their [documentation](https://rustwasm.github.io/docs/wasm-bindgen/)
We will also expand upon this concept in the [more wasm-bindgen](../wasm-bindgen).
## web-sys
The [`web-sys` crate](https://crates.io/crates/web-sys) provides bindings for Web APIs and allows us to write Javascript code in a rustyfied and safe way.
Example:
<Tabs>
<TabItem value="JS" label="JS">
```js
let document = window.document;
```
</TabItem>
<TabItem value="RS" label="RS">
```rust ,no_run
use wasm_bindgen::UnwrapThrowExt;
use web_sys::window;
let document = window()
.expect_throw("window is undefined")
.document()
.expect_throw("document is undefined");
```
</TabItem>
</Tabs>
Once again we highly recommend you give a look to their [documentation](https://rustwasm.github.io/docs/wasm-bindgen/)
We will also expand upon this concept in the [more wasm-bindgen](../wasm-bindgen).

View File

@ -4,11 +4,12 @@ sidebar_label: Contexts
description: "Using contexts to pass data within application"
---
Generally data is passed down the component tree using props but that becomes tedious for values such as
user preferences, authentication information etc. Consider the following example which passes down the
Generally data is passed down the component tree using props but that becomes tedious for values such as
user preferences, authentication information etc. Consider the following example which passes down the
theme using props:
```rust
use yew::{html, Children, Component, Context, Html, Properties};
use yew::{html, Children, Component, Context, Html, Properties, function_component};
#[derive(Clone, PartialEq)]
pub struct Theme {
@ -21,27 +22,17 @@ pub struct NavbarProps {
theme: Theme,
}
pub struct Navbar;
impl Component for Navbar {
type Message = ();
type Properties = NavbarProps;
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<Title theme={ctx.props().theme.clone()}>
{ "App title" }
</Title>
<NavButton theme={ctx.props().theme.clone()}>
{ "Somewhere" }
</NavButton>
</div>
}
#[function_component]
fn Navbar(props: &NavbarProps) -> Html {
html! {
<div>
<Title theme={props.theme.clone()}>
{ "App title" }
</Title>
<NavButton theme={props.theme.clone()}>
{ "Somewhere" }
</NavButton>
</div>
}
}
@ -51,14 +42,14 @@ pub struct ThemeProps {
children: Children,
}
#[yew::function_component(Title)]
fn title(_props: &ThemeProps) -> Html {
#[function_component]
fn Title(_props: &ThemeProps) -> Html {
html! {
// impl
}
}
#[yew::function_component(NavButton)]
fn nav_button(_props: &ThemeProps) -> Html {
#[function_component]
fn NavButton(_props: &ThemeProps) -> Html {
html! {
// impl
}
@ -75,16 +66,17 @@ html! {
};
```
Passing down data like this isn't ideal for something like a theme which needs to be available everywhere.
Passing down data like this isn't ideal for something like a theme which needs to be available everywhere.
This is where contexts come in.
Contexts provide a way to share data between components without passing them down explicitly as props.
They make data available to all components in the tree.
Contexts can be understood as a global-ish opt in props.
You need to provide the value somewhere in the component tree and all sub-components will be able to listen into its value and changes.
## Using Contexts
In order to use contexts, we need a struct which defines what data is to be passed.
For the above use-case, consider the following struct:
```rust
#[derive(Clone, Debug, PartialEq)]
struct Theme {
@ -101,49 +93,9 @@ The children are re-rendered when the context changes.
#### Struct components
The `Scope::context` method is used to consume contexts in struct components.
##### Example
```rust
use yew::{Callback, html, Component, Context, Html};
#[derive(Clone, Debug, PartialEq)]
struct Theme {
foreground: String,
background: String,
}
struct ContextDemo;
impl Component for ContextDemo {
type Message = ();
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, ctx: &Context<Self>) -> Html {
let (theme, _) = ctx
.link()
.context::<Theme>(Callback::noop())
.expect("context to be set");
html! {
<button style={format!(
"background: {}; color: {};",
theme.background,
theme.foreground
)}
>
{ "Click me!" }
</button>
}
}
}
```
Use [HOC](../advanced-topics/struct-components/hoc)
#### Function components
`use_context` hook is used to consume contexts in function components.
See [docs for use_context](function-components/pre-defined-hooks.mdx#use_context) to learn more.
`use_context` hook is used to consume contexts in function components.
See [docs for use_context](function-components/hooks/use-context) to learn more.

View File

@ -1,142 +0,0 @@
---
title: "#[function_component]"
description: "The #[function_component] attribute"
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
`#[function_component(_)]` turns a normal Rust function into a function component.
Functions with the attribute have to return `Html` and may take a single parameter for the type of props the component should accept.
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.
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
use yew::{function_component, html, Html};
#[function_component(ChatContainer)]
pub fn chat_container() -> Html {
html! {
// chat container impl
}
}
html! {
<ChatContainer />
};
```
## Example
<Tabs>
<TabItem value="With props" label="With props">
```rust
use yew::{function_component, html, Properties, Html};
#[derive(Properties, PartialEq)]
pub struct RenderedAtProps {
pub time: String,
}
#[function_component]
pub fn RenderedAt(props: &RenderedAtProps) -> Html {
html! {
<p>
<b>{ "Rendered at: " }</b>
{ &props.time }
</p>
}
}
```
</TabItem>
<TabItem value="Without props" label="Without props">
```rust
use yew::{function_component, html, use_state, Callback, Html};
#[function_component]
fn App() -> Html {
let counter = use_state(|| 0);
let onclick = {
let counter = counter.clone();
Callback::from(move |_| counter.set(*counter + 1))
};
html! {
<div>
<button {onclick}>{ "Increment value" }</button>
<p>
<b>{ "Current value: " }</b>
{ *counter }
</p>
</div>
}
}
```
</TabItem>
</Tabs>
## Generic function components
The `#[function_component(_)]` attribute also works with generic functions for creating generic components.
```rust title=my_generic_component.rs
use std::fmt::Display;
use yew::{function_component, html, Properties, Html};
#[derive(Properties, PartialEq)]
pub struct Props<T>
where
T: PartialEq,
{
data: T,
}
#[function_component]
pub fn MyGenericComponent<T>(props: &Props<T>) -> Html
where
T: PartialEq + Display,
{
html! {
<p>
{ &props.data }
</p>
}
}
// used like this
html! {
<MyGenericComponent<i32> data=123 />
};
// or
html! {
<MyGenericComponent<String> data={"foo".to_string()} />
};
```

View File

@ -0,0 +1,72 @@
---
title: "Callbacks"
---
Callbacks used to asynchronously communicate in Yew upwards the components tree and other things like agents or DOM.
Internally their type is just `Fn` wrapped in `Rc` to allow them to be cloned. You will need to clone them often.
They have an `emit` function that takes their `<IN, OUT = ()>` types as an arguments.
```rust
use yew::{html, Component, Context, Html, Callback};
let cb: Callback<String, String> = Callback::from(move |name: String| {
format!("Bye {}", name)
});
let result = cb.emit(String::from("Bob")); // call the callback
// web_sys::console::log_1(&result.into()); // if uncommented will print "Bye Bob"
```
## Passing callbacks as props
A common pattern in yew is to create a callback and pass it down as a prop.
```rust
use yew::{function_component, html, Html, Properties, Callback};
#[derive(Properties, PartialEq)]
pub struct Props {
pub on_name_entry: Callback<String>,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
props.on_name_entry.emit(String::from("Bob"));
html! { "Hello" }
}
// Then supply the prop
#[function_component]
fn App() -> Html {
let on_name_entry: Callback<String> = Callback::from(move |name: String| {
let greeting = format!("Hey, {}!", name);
// web_sys::console::log_1(&greeting.into()); // if uncommented will print
});
html! {<HelloWorld {on_name_entry} />}
}
```
## DOM Events and Callbacks
Callbacks are also used to hook into DOM events.
For example here we define a callback that will be called when user clicks the button:
```rust
use yew::{function_component, html, Html, Properties, Callback};
#[function_component]
fn App() -> Html {
let onclick = Callback::from(move |_| {
let greeting = String::from("Hi there");
// web_sys::console::log_1(&greeting.into()); // if uncommented will print
});
html! {<button {onclick}>{ "Click" }</button>}
}
```

View File

@ -0,0 +1,42 @@
---
title: "Children"
---
`Children` is a special prop type that allows you to take what was provided as `Html` children.
```rust
use yew::{function_component, html, Html, Properties, Children};
#[function_component]
fn App() -> Html {
html! {
// highlight-start
<HelloWorld>
<span>{"Hey what is up ;)"}</span>
<h1>{"THE SKY"}</h1>
</HelloWorld>
// highlight-end
}
}
#[derive(Properties, PartialEq)]
pub struct Props {
// highlight-next-line
pub children: Children,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
html! {
<div class="very-stylized-container">
// highlight-next-line
{ for props.children.iter() } //then use like this
</div>
}
}
```
## Further reading
- [Advanced ways to handle children](../../advanced-topics/children)

View File

@ -0,0 +1,11 @@
---
title: "Communication between components"
---
## Parent to child messaging
Pass data as [props](./properties) that cause a rerender, this is the way to pass messages to children.
## Child to parent messaging
Pass down a callback via props, that the child on an event can call. [Example](callbacks#passing-callbacks-as-props)

View File

@ -0,0 +1,44 @@
---
title: "Generic Components"
description: "The #[function_component] attribute"
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
The `#[function_component]` attribute also works with generic functions for creating generic components.
```rust
use std::fmt::Display;
use yew::{function_component, html, Properties, Html};
#[derive(Properties, PartialEq)]
pub struct Props<T>
where
T: PartialEq,
{
data: T,
}
#[function_component]
pub fn MyGenericComponent<T>(props: &Props<T>) -> Html
where
T: PartialEq + Display,
{
html! {
<p>
{ &props.data }
</p>
}
}
// then can be used like this
html! {
<MyGenericComponent<i32> data=123 />
};
// or
html! {
<MyGenericComponent<String> data={"foo".to_string()} />
};
```

View File

@ -1,6 +1,5 @@
---
title: "Custom Hooks"
description: "Defining your own Hooks "
---
## Defining custom Hooks
@ -42,6 +41,7 @@ We'll start by creating a new function called `use_event`.
The `use_` prefix denotes that a function is a hook.
This function will take an event target, a event type and a callback.
All hooks must be marked by `#[hook]` to function as as hook.
```rust
use web_sys::{Event, EventTarget};
use std::borrow::Cow;

View File

@ -0,0 +1,47 @@
---
title: "Hooks"
slug: /concepts/function-components/hooks
---
## Hooks
Hooks are functions that let you store state and perform side-effects.
Yew comes with a few pre-defined Hooks. You can also create your own or discover many [community made hooks](/community/awesome#hooks).
## Rules of hooks
1. A hook function name always has to start with `use_`
2. Hooks can only be used at the following locations:
- Top level of a function / hook.
- If condition inside a function / hook, given it's not already branched.
- Match condition inside a function / hook, given it's not already branched.
- Blocks inside a function / hook, given it's not already branched.
3. Hooks every render must be called in the same order
All these rules are enforced by either compile time or run-time errors.
### Pre-defined Hooks
Yew comes with the following predefined Hooks:
- [`use_state`](./pre-defined-hooks.mdx#use_state)
- [`use_state_eq`](./pre-defined-hooks.mdx#use_state_eq)
- [`use_memo`](./pre-defined-hooks.mdx#use_memo)
- [`use_mut_ref`](./pre-defined-hooks.mdx#use_mut_ref)
- [`use_node_ref`](./pre-defined-hooks.mdx#use_node_ref)
- [`use_reducer`](./pre-defined-hooks.mdx#use_reducer)
- [`use_reducer_eq`](./pre-defined-hooks.mdx#use_reducer_eq)
- [`use_effect`](./pre-defined-hooks.mdx#use_effect)
- [`use_effect_with_deps`](./pre-defined-hooks.mdx#use_effect_with_deps)
- [`use_context`](./pre-defined-hooks.mdx#use_context)
### Custom Hooks
There are cases where you want to define your own Hooks for reasons. Yew allows you to define your own Hooks which lets you extract your potentially stateful logic from the component into reusable functions.
See the [Defining custom hooks](./custom-hooks.mdx#defining-custom-hooks) section for more information.
## Further reading
- The React documentation has a section on [React hooks](https://reactjs.org/docs/hooks-intro.html).
These are not exactly the same as Yew's hooks, but the underlying concept is similar.

View File

@ -0,0 +1,65 @@
---
title: "use_context"
---
[Contexts](../contexts.mdx) will be introduced later.
`use_context` is used for consuming [contexts](../contexts.mdx) in function components.
## Example
```rust
use yew::{ContextProvider, function_component, html, use_context, use_state, Html};
/// App theme
#[derive(Clone, Debug, PartialEq)]
struct Theme {
foreground: String,
background: String,
}
/// Main component
#[function_component]
pub fn App() -> Html {
let ctx = use_state(|| Theme {
foreground: "#000000".to_owned(),
background: "#eeeeee".to_owned(),
});
html! {
// `ctx` is type `Rc<UseStateHandle<Theme>>` while we need `Theme`
// so we deref it.
// It derefs to `&Theme`, hence the clone
<ContextProvider<Theme> context={(*ctx).clone()}>
// Every child here and their children will have access to this context.
<Toolbar />
</ContextProvider<Theme>>
}
}
/// The toolbar.
/// This component has access to the context
#[function_component]
pub fn Toolbar() -> Html {
html! {
<div>
<ThemedButton />
</div>
}
}
/// Button placed in `Toolbar`.
/// As this component is a child of `ThemeContextProvider` in the component tree, it also has access to the context.
#[function_component]
pub fn ThemedButton() -> Html {
//highlight-next-line
let theme = use_context::<Theme>().expect("no ctx found");
html! {
<button style={format!("background: {}; color: {};", theme.background, theme.foreground)}>
{ "Click me!" }
</button>
}
}
```

View File

@ -0,0 +1,160 @@
---
title: "use_effect"
---
`use_effect` is used for hooking into the component's lifecycle and creating side-effects.
It takes a function which is called after every component's render finishes.
The input function has to return a closure - a cleanup function, which is called right before starting a new render.
## Example
```rust
use yew::{Callback, function_component, html, use_effect, use_state, Html};
#[function_component]
fn EffectExample() -> Html {
let counter = use_state(|| 0);
{
let counter = counter.clone();
use_effect(move || {
// Make a call to DOM API after component is rendered
gloo::utils::document().set_title(&format!("You clicked {} times", *counter));
// Perform the cleanup
|| gloo::utils::document().set_title("You clicked 0 times")
});
}
let onclick = {
let counter = counter.clone();
Callback::from(move |_| counter.set(*counter + 1))
};
html! {
<button {onclick}>{ format!("Increment to {}", *counter) }</button>
}
}
```
## `use_effect_with_deps`
`use_effect_with_deps` is a more enhanced version of [`use_effect`](#use_effect).
It takes a second argument - dependencies.
Only when dependencies change, it calls the provided function.
Same way with the clean-up function - it gets called only before re-calling the function from the first argument.
:::note
`dependencies` must implement `PartialEq`.
:::
```rust
use yew::{function_component, html, Html, Properties, use_effect_with_deps};
#[derive(Properties, PartialEq)]
pub struct Props {
pub is_loading: bool,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
let is_loading = props.is_loading.clone();
use_effect_with_deps(
move |_| {
web_sys::console::log_1(&" Is loading prop changed!".into());
|| ()
},
// highlight-next-line
is_loading,
);
html! { <>{"Am I loading? - "}{is_loading}</> }
}
```
## Tips
### Only on first render
Provide a empty tuple `()` as dependencies when you need to do something only on first render.
```rust
use yew::{function_component, html, Html, use_effect_with_deps};
#[function_component]
fn HelloWorld() -> Html {
use_effect_with_deps(
move |_| {
web_sys::console::log_1(&"I got rendered, yay!".into());
|| ()
},
// highlight-next-line
(),
);
html! { "Hello" }
}
```
### On destructing or last render
Use [Only on first render](#only-on-first-render) and write the code in the cleanup function.
It will only get called when the component is removed from view / gets destroyed.
```rust
use yew::{function_component, html, Html, use_effect_with_deps};
#[function_component]
fn HelloWorld() -> Html {
use_effect_with_deps(
move |_| {
// move is not needed bellow but the docs page breaks without it
move || {
web_sys::console::log_1(&"Noo dont kill me, ahhh!".into());
}
},
(),
);
html! { "Hello" }
}
```
### Applying event listeners to the DOM
Generally you can directly add the `on` + event name as just a html attribute.
But for cases when you need to do it manually always re-apply and destroy old event listeners every render to avoid duplicate listeners in the DOM.
Same tip applies to intervals and timeouts.
```rust
use yew::{function_component, html, Html, use_effect_with_deps, use_node_ref};
#[function_component]
fn HelloWorld() -> Html {
let span_ref = use_node_ref();
{
let span_ref = span_ref.clone();
use_effect_with_deps(
move |_| {
let listener = gloo::events::EventListener::new(&span_ref.cast::<web_sys::HtmlElement>().unwrap(), "click", move |event| {
web_sys::console::log_1(&"I got clicked".into());
});
move || drop(listener)
},
(),
);
}
html! {<span ref={span_ref}>{"Hello"}</span>}
}
```

View File

@ -0,0 +1,34 @@
---
title: "use_memo"
---
`use_memo` is used for obtaining an immutable reference to a memoized value.
Its state persists across renders.
Its value will be recalculated only if any of the dependencies values change.
`use_memo` can be useful for keeping things in scope for the lifetime of the component, so long as
you don't store a clone of the resulting `Rc` anywhere that outlives the component.
```rust
use yew::{function_component, html, use_memo, use_state, Callback, Html, Properties};
#[derive(PartialEq, Properties)]
pub struct Props {
pub step: usize,
}
#[function_component(UseMemo)]
fn ref_hook(props: &Props) -> Html {
// Will only get recalculated if `props.step` value changes
let message = use_memo(
|step| format!("{}. Do Some Expensive Calculation", step),
props.step
);
html! {
<div>
<span>{ (*message).clone() }</span>
</div>
}
}
```

View File

@ -0,0 +1,55 @@
---
title: "use_mut_ref"
---
`use_mut_ref` is used for obtaining a mutable reference to a value.
Its state persists across renders.
It is important to note that you do not get notified of state changes.
If you need the component to be re-rendered on state change, consider using [`use_state`](./use-state).
## Example
Here you can see we count `message_count` as that number is not used in `Html` and thus does not need to cause a rerender.
```rust
use web_sys::HtmlInputElement;
use yew::{
events::Event,
function_component, html, use_mut_ref, use_state,
Callback, TargetCast,
Html,
};
#[function_component]
fn MutRefExample() -> Html {
let message = use_state(|| "".to_string());
let message_count = use_mut_ref(|| 0);
let onclick = Callback::from(move |_| {
let window = gloo::utils::window();
if *message_count.borrow_mut() > 3 {
window.alert_with_message("Message limit reached").unwrap();
} else {
*message_count.borrow_mut() += 1;
window.alert_with_message("Message sent").unwrap();
}
});
let onchange = {
let message = message.clone();
Callback::from(move |e: Event| {
let input: HtmlInputElement = e.target_unchecked_into();
message.set(input.value());
})
};
html! {
<div>
<input {onchange} value={(*message).clone()} />
<button {onclick}>{ "Send" }</button>
</div>
}
}
```

View File

@ -0,0 +1,50 @@
---
title: "use_node_ref"
---
`use_node_ref` is used for maintaining an easy reference to the DOM element represented as a `NodeRef`. It also persists across renders.
## Example
```rust
use web_sys::HtmlInputElement;
use yew::{
function_component, functional::*, html,
NodeRef, Html
};
#[function_component(UseRef)]
pub fn ref_hook() -> Html {
// highlight-next-line
let input_ref = use_node_ref();
let value = use_state(|| 25_f64);
let onclick = {
let input_ref = input_ref.clone();
let value = value.clone();
move |_| {
// highlight-start
if let Some(input) = input_ref.cast::<HtmlInputElement>() {
value.set(*value + input.value_as_number());
}
// highlight-end
}
};
html! {
<div>
// highlight-next-line
<input ref={input_ref} type="number" />
<button {onclick}>{ format!("Add input to {}", *value) }</button>
</div>
}
}
```
:::tip Advanced tip
When conditionally rendering elements you can use `NodeRef` in conjunction with `use_effect_with_deps`
to perform actions each time an element is rendered and just before its going to be removed from the
DOM.
:::

View File

@ -0,0 +1,109 @@
---
title: "use_reducer"
---
`use_reducer` is an alternative to [`use_state`](./use-state). It is used to handle more complex component's state.
It accepts an initial state function and returns a `UseReducerHandle` that dereferences to the state,
and a dispatch function.
The dispatch function takes one argument of type `Action`. When called, the action and current value
are passed to the reducer function which computes a new state that is returned,
and the component is re-rendered.
:::tip Advanced tip
The dispatch function is guaranteed to be the same across the entire
component lifecycle. You can safely omit the `UseReducerHandle` from the
dependents of `use_effect_with_deps` if you only intend to dispatch
values from within the hooks.
:::
The state object returned by the initial state function is required to
implement a `Reducible` trait which provides an `Action` type and a
reducer function.
This hook will always trigger a re-render upon receiving an action. See
[`use_reducer_eq`](#use_reducer_eq) if you want the component to only
re-render when the state changes.
## Example
```rust
use yew::prelude::*;
use std::rc::Rc;
/// reducer's Action
enum CounterAction {
Double,
Square,
}
/// reducer's State
struct CounterState {
counter: i32,
}
impl Default for CounterState {
fn default() -> Self {
Self { counter: 1 }
}
}
impl Reducible for CounterState {
/// Reducer Action Type
type Action = CounterAction;
/// Reducer Function
fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> {
let next_ctr = match action {
CounterAction::Double => self.counter * 2,
CounterAction::Square => self.counter.pow(2)
};
Self { counter: next_ctr }.into()
}
}
#[function_component]
fn ReducerExample() -> Html {
// The use_reducer hook takes an initialization function which will be called only once.
let counter = use_reducer(CounterState::default);
let double_onclick = {
let counter = counter.clone();
Callback::from(move |_| counter.dispatch(CounterAction::Double))
};
let square_onclick = {
let counter = counter.clone();
Callback::from(move |_| counter.dispatch(CounterAction::Square))
};
html! {
<>
<div id="result">{ counter.counter }</div>
<button onclick={double_onclick}>{ "Double" }</button>
<button onclick={square_onclick}>{ "Square" }</button>
</>
}
}
```
:::caution
The value held in the handle will reflect the value of at the time the
handle is returned by the `use_reducer`. It is possible that the handle does
not dereference to an up to date value if you are moving it into a
`use_effect_with_deps` hook. You can register the
state to the dependents so the hook can be updated when the value changes.
:::
## `use_reducer_eq`
This hook has the same effect as `use_reducer` but will only trigger a
re-render when the reducer function produces a value that `prev_state != next_state`.
This hook requires the state object to implement `PartialEq` in addition
to the `Reducible` trait required by `use_reducer`.

View File

@ -0,0 +1,67 @@
---
title: "use_state"
---
`use_state` is used to manage state in a function component.
It returns a `UseStateHandle` object which `Deref`s to the currently stored value
and provides a `set` method to update the value.
The hook takes a function as input which determines the initial value.
:::tip Advanced tip
The setter function is guaranteed to be the same across the entire
component lifecycle. You can safely omit the `UseStateHandle` from the
dependents of `use_effect_with_deps` if you only intend to set
values from within the hook.
:::
This hook will always trigger a re-render upon receiving a new state. See
[`use_state_eq`](#use_state_eq) if you want the component to only
re-render when the state changes.
## Example
```rust
use std::ops::Deref;
use yew::{Callback, function_component, html, use_state, Html};
#[function_component]
fn StateExample() -> Html {
let name_handle = use_state(|| String::from("Bob"));
let name = name_handle.deref().clone();
let onclick = {
let name = name.clone();
Callback::from(move |_| name_handle.set(format!("{}y Jr.", name)))
};
html! {
<div>
<button {onclick}>{ "Update name" }</button>
<p>
<b>{ "My name is: " }</b>
{ name }
</p>
</div>
}
}
```
:::caution
The value held in the handle will reflect the value at the time the
handle is returned by the `use_state`. It is possible that the handle
does not dereference to an up to date value if you are moving it into a
`use_effect_with_deps` hook. You can register the
state to the dependents so the hook can be updated when the value changes.
:::
## `use_state_eq`
This hook has the same effect as `use_state` but will only trigger a
re-render when the setter receives a value that `prev_state != next_state`.
This hook requires the state object to implement `PartialEq`.

View File

@ -1,19 +1,35 @@
---
title: "Function Components"
sidebar_label: Introduction
description: "Introduction to function components "
slug: /concepts/function-components
---
Function components are a simplified version of normal components. They consist of a single function
that receives props and determines what should be rendered by returning `Html`. Basically, it's a
component that's been reduced to just the `view` method. On its own that would be quite limiting
because you can only create pure components, but that's where Hooks come in. Hooks allow function
components to maintain their own internal state and use other Yew features without needing to manually
implement the `Component` trait.
Lets revisit this previous statement:
> Yew mostly operates on the idea of keeping everything that a reusable piece of
> UI may need, in one place - rust files.
It is only partially correct, "rust files" should be replaced with "components".
## What are Components?
Components are the building blocks of Yew.
They:
- Take arguments in form of [Props](./properties)
- Can have their own state
- Get computed into HTML visible to the user (DOM)
## Two flavours of Yew Components
You are currently reading about function components - the recommended way to write components when starting with Yew.
But we have to note that there is a more advanced, but less recommended way to write them - [Struct components](../advanced-topics/struct-components/introduction)
## Creating function components
The easiest way to create a function component is to add the [`#[function_component]`](./../function-components/attribute.mdx) attribute to a function.
To create a function component add the `#[function_component]` attribute to a function.
Also name the function in PascalCase as it is the convention for naming components.
```rust
use yew::{function_component, html, Html};
@ -22,50 +38,32 @@ use yew::{function_component, html, Html};
fn HelloWorld() -> Html {
html! { "Hello world" }
}
// Then somewhere else you can use the component inside the `html!`
#[function_component]
fn App() -> Html {
html! {<HelloWorld />}
}
```
### Under the hood
## What happens to components
There are two parts to how Yew implements function components.
Yew will build a tree of these components that from the previous example would look like this:
The first part is the `FunctionProvider` trait which is analogous to the `Component` trait, except
that it only has a single method (called `run`). The second part is the `FunctionComponent` struct
which wraps types implementing `FunctionProvider` and implements `Component`.
```md
<App />
|
<HelloWorld />
```
The `#[function_component]` attribute is a procedural macro which automatically implements
`FunctionProvider` for you and exposes it wrapped in `FunctionComponent`.
It will call those functions / function components to compute a virtual version of the DOM (VDOM) that you as the library user see as the `Html` type.
### Hooks
Yew will compare current DOM with VDOM and only update the new/changed/necessary parts.
Hooks are functions that let you "hook into" components' state and/or lifecycle and perform
actions. Yew comes with a few pre-defined Hooks. You can also create your own.
This is what we call **rendering**.
Hooks can only be used at the following locations:
- Top level of a function / hook.
- If condition inside a function / hook, given it's not already branched.
- Match condition inside a function / hook, given it's not already branched.
- Blocks inside a function / hook, given it's not already branched.
:::note
#### Pre-defined Hooks
Behind the scenes `Html` type is just an alias for `VNode` - virtual node.
Yew comes with the following predefined Hooks:
- [`use_state`](./../function-components/pre-defined-hooks.mdx#use_state)
- [`use_state_eq`](./../function-components/pre-defined-hooks.mdx#use_state_eq)
- [`use_memo`](./../function-components/pre-defined-hooks.mdx#use_memo)
- [`use_mut_ref`](./../function-components/pre-defined-hooks.mdx#use_mut_ref)
- [`use_node_ref`](./../function-components/pre-defined-hooks.mdx#use_node_ref)
- [`use_reducer`](./../function-components/pre-defined-hooks.mdx#use_reducer)
- [`use_reducer_eq`](./../function-components/pre-defined-hooks.mdx#use_reducer_eq)
- [`use_effect`](./../function-components/pre-defined-hooks.mdx#use_effect)
- [`use_effect_with_deps`](./../function-components/pre-defined-hooks.mdx#use_effect_with_deps)
- [`use_context`](./../function-components/pre-defined-hooks.mdx#use_context)
#### Custom Hooks
There are cases where you want to define your own Hooks for reasons. Yew allows you to define your own Hooks which lets you extract your potentially stateful logic from the component into reusable functions.
See the [Defining custom hooks](./../function-components/custom-hooks.mdx#defining-custom-hooks) section for more information.
## Further reading
* The React documentation has a section on [React hooks](https://reactjs.org/docs/hooks-intro.html).
These are not exactly the same as Yew's hooks, but the underlying concept is similar.
:::

View File

@ -0,0 +1,14 @@
---
title: "Node Refs"
description: "Out-of-band DOM access"
---
The `ref` keyword can be used inside of any HTML element or component to get the DOM `Element` that
the item is attached to. This can be used to make changes to the DOM outside of the `view` lifecycle
method.
This is useful for getting ahold of canvas elements, or scrolling to different sections of a page.
## Example
See [use_node_ref hook](./hooks/use-node-ref)

View File

@ -1,411 +0,0 @@
---
title: "Pre-defined Hooks"
description: "The pre-defined Hooks that Yew comes with "
---
## `use_state`
`use_state` is used to manage state in a function component.
It returns a `UseStateHandle` object which `Deref`s to the current value
and provides a `set` method to update the value.
The hook takes a function as input which determines the initial state.
This value remains up-to-date on subsequent renders.
The setter function is guaranteed to be the same across the entire
component lifecycle. You can safely omit the `UseStateHandle` from the
dependents of `use_effect_with_deps` if you only intend to set
values from within the hook.
This hook will always trigger a re-render upon receiving a new state. See
[`use_state_eq`](#use_state_eq) if you want the component to only
re-render when the state changes.
### Example
```rust
use yew::{Callback, function_component, html, use_state, Html};
#[function_component(UseState)]
fn state() -> Html {
let counter = use_state(|| 0);
let onclick = {
let counter = counter.clone();
Callback::from(move |_| counter.set(*counter + 1))
};
html! {
<div>
<button {onclick}>{ "Increment value" }</button>
<p>
<b>{ "Current value: " }</b>
{ *counter }
</p>
</div>
}
}
```
:::caution
The value held in the handle will reflect the value at the time the
handle is returned by the `use_state`. It is possible that the handle
does not dereference to an up to date value if you are moving it into a
`use_effect_with_deps` hook. You can register the
state to the dependents so the hook can be updated when the value changes.
:::
## `use_state_eq`
This hook has the same effect as `use_state` but will only trigger a
re-render when the setter receives a value that `prev_state != next_state`.
This hook requires the state object to implement `PartialEq`.
## `use_memo`
`use_memo` is used for obtaining an immutable reference to a memoized value.
Its state persists across renders.
Its value will be recalculated only if any of the dependencies values change.
`use_memo` can be useful for keeping things in scope for the lifetime of the component, so long as
you don't store a clone of the resulting `Rc` anywhere that outlives the component.
```rust
use yew::{function_component, html, use_memo, use_state, Callback, Html, Properties};
#[derive(PartialEq, Properties)]
pub struct Props {
pub step: usize,
}
#[function_component(UseMemo)]
fn ref_hook(props: &Props) -> Html {
// Will only get recalculated if `props.step` value changes
let message = use_memo(
|step| format!("{}. Do Some Expensive Calculation", step),
props.step
);
html! {
<div>
<span>{ (*message).clone() }</span>
</div>
}
}
```
## `use_mut_ref`
`use_mut_ref` is used for obtaining a mutable reference to a value.
Its state persists across renders.
It is important to note that you do not get notified of state changes.
If you need the component to be re-rendered on state change, consider using [`use_state`](#use_state).
### Example
```rust
use web_sys::HtmlInputElement;
use yew::{
events::Event,
function_component, html, use_mut_ref, use_state,
Callback, TargetCast,
Html,
};
#[function_component(UseMutRef)]
fn mut_ref_hook() -> Html {
let message = use_state(|| "".to_string());
let message_count = use_mut_ref(|| 0);
let onclick = Callback::from(move |_| {
let window = gloo::utils::window();
if *message_count.borrow_mut() > 3 {
window.alert_with_message("Message limit reached").unwrap();
} else {
*message_count.borrow_mut() += 1;
window.alert_with_message("Message sent").unwrap();
}
});
let onchange = {
let message = message.clone();
Callback::from(move |e: Event| {
let input: HtmlInputElement = e.target_unchecked_into();
message.set(input.value());
})
};
html! {
<div>
<input {onchange} value={(*message).clone()} />
<button {onclick}>{ "Send" }</button>
</div>
}
}
```
## `use_node_ref`
`use_node_ref` is used for obtaining a `NodeRef` that persists across renders.
When conditionally rendering elements you can use `NodeRef` in conjunction with `use_effect_with_deps`
to perform actions each time an element is rendered and just before its going to be removed from the
DOM.
### Example
```rust
use web_sys::HtmlInputElement;
use yew::{
function_component, functional::*, html,
NodeRef, Html
};
#[function_component(UseRef)]
pub fn ref_hook() -> Html {
let input_ref = use_node_ref();
let value = use_state(|| 25f64);
let onclick = {
let input_ref = input_ref.clone();
let value = value.clone();
move |_| {
if let Some(input) = input_ref.cast::<HtmlInputElement>() {
value.set(*value + input.value_as_number());
}
}
};
html! {
<div>
<input ref={input_ref} type="number" />
<button {onclick}>{ format!("Add input to {}", *value) }</button>
</div>
}
}
```
## `use_reducer`
`use_reducer` is an alternative to [`use_state`](#use_state). It is used to handle component's state and is used
when complex actions needs to be performed on said state.
It accepts an initial state function and returns a `UseReducerHandle` that dereferences to the state,
and a dispatch function.
The dispatch function takes one argument of type `Action`. When called, the action and current value
are passed to the reducer function which computes a new state which is returned,
and the component is re-rendered.
The dispatch function is guaranteed to be the same across the entire
component lifecycle. You can safely omit the `UseReducerHandle` from the
dependents of `use_effect_with_deps` if you only intend to dispatch
values from within the hooks.
The state object returned by the initial state function is required to
implement a `Reducible` trait which provides an `Action` type and a
reducer function.
This hook will always trigger a re-render upon receiving an action. See
[`use_reducer_eq`](#use_reducer_eq) if you want the component to only
re-render when the state changes.
### Example
```rust
use yew::prelude::*;
use std::rc::Rc;
/// reducer's Action
enum CounterAction {
Double,
Square,
}
/// reducer's State
struct CounterState {
counter: i32,
}
impl Default for CounterState {
fn default() -> Self {
Self { counter: 1 }
}
}
impl Reducible for CounterState {
/// Reducer Action Type
type Action = CounterAction;
/// Reducer Function
fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> {
let next_ctr = match action {
CounterAction::Double => self.counter * 2,
CounterAction::Square => self.counter.pow(2)
};
Self { counter: next_ctr }.into()
}
}
#[function_component(UseReducer)]
fn reducer() -> Html {
// The use_reducer hook takes an initialization function which will be called only once.
let counter = use_reducer(CounterState::default);
let double_onclick = {
let counter = counter.clone();
Callback::from(move |_| counter.dispatch(CounterAction::Double))
};
let square_onclick = {
let counter = counter.clone();
Callback::from(move |_| counter.dispatch(CounterAction::Square))
};
html! {
<>
<div id="result">{ counter.counter }</div>
<button onclick={double_onclick}>{ "Double" }</button>
<button onclick={square_onclick}>{ "Square" }</button>
</>
}
}
```
:::caution
The value held in the handle will reflect the value of at the time the
handle is returned by the `use_reducer`. It is possible that the handle does
not dereference to an up to date value if you are moving it into a
`use_effect_with_deps` hook. You can register the
state to the dependents so the hook can be updated when the value changes.
:::
## `use_reducer_eq`
This hook has the same effect as `use_reducer` but will only trigger a
re-render when the reducer function produces a value that `prev_state != next_state`.
This hook requires the state object to implement `PartialEq` in addition
to the `Reducible` trait required by `use_reducer`.
## `use_effect`
`use_effect` is used for hooking into the component's lifecycle.
Similar to `rendered` from the `Component` trait,
`use_effect` takes a function which is called after the render finishes.
The input function has to return a closure, the destructor, which is called when the component is destroyed.
The destructor can be used to clean up the effects introduced and it can take ownership of values to delay dropping them until the component is destroyed.
### Example
```rust
use yew::{Callback, function_component, html, use_effect, use_state, Html};
#[function_component(UseEffect)]
fn effect() -> Html {
let counter = use_state(|| 0);
{
let counter = counter.clone();
use_effect(move || {
// Make a call to DOM API after component is rendered
gloo::utils::document().set_title(&format!("You clicked {} times", *counter));
// Perform the cleanup
|| gloo::utils::document().set_title("You clicked 0 times")
});
}
let onclick = {
let counter = counter.clone();
Callback::from(move |_| counter.set(*counter + 1))
};
html! {
<button {onclick}>{ format!("Increment to {}", *counter) }</button>
}
}
```
### `use_effect_with_deps`
Sometimes, it's needed to manually define dependencies for [`use_effect`](#use_effect). In such cases, we use `use_effect_with_deps`.
```rust ,no_run
use yew::use_effect_with_deps;
use_effect_with_deps(
move |_| {
// ...
|| ()
},
(), // dependents
);
```
**Note**: `dependents` must implement `PartialEq`.
## `use_context`
`use_context` is used for consuming [contexts](../contexts.mdx) in function components.
### Example
```rust
use yew::{ContextProvider, function_component, html, use_context, use_state, Html};
/// App theme
#[derive(Clone, Debug, PartialEq)]
struct Theme {
foreground: String,
background: String,
}
/// Main component
#[function_component(App)]
pub fn app() -> Html {
let ctx = use_state(|| Theme {
foreground: "#000000".to_owned(),
background: "#eeeeee".to_owned(),
});
html! {
// `ctx` is type `Rc<UseStateHandle<Theme>>` while we need `Theme`
// so we deref it.
// It derefs to `&Theme`, hence the clone
<ContextProvider<Theme> context={(*ctx).clone()}>
// Every child here and their children will have access to this context.
<Toolbar />
</ContextProvider<Theme>>
}
}
/// The toolbar.
/// This component has access to the context
#[function_component(Toolbar)]
pub fn toolbar() -> Html {
html! {
<div>
<ThemedButton />
</div>
}
}
/// Button placed in `Toolbar`.
/// As this component is a child of `ThemeContextProvider` in the component tree, it also has access to the context.
#[function_component(ThemedButton)]
pub fn themed_button() -> Html {
let theme = use_context::<Theme>().expect("no ctx found");
html! {
<button style={format!("background: {}; color: {};", theme.background, theme.foreground)}>
{ "Click me!" }
</button>
}
}
```

View File

@ -0,0 +1,258 @@
---
title: "Properties"
description: "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 function arguments that Yew can keep watch on.
Components can have an associated properties type which describes what is passed down from the parent.
Props are defined using the `Properties` trait.
## Reactivity
Yew always checks if props changed to know if the component needs to be rerendered.
This way Yew can be considered a very reactive framework as any change 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](../../tutorial), try it out and test this reactivity yourself!
:::
## Memory/speed overhead of using Properties
Internally properties are reference counted. This means that only a 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.
## Derive macro
Yew provides a derive macro to easily implement the `Properties` trait.
Types for which you derive `Properties` must also implement `PartialEq` so Yew can do data comparison.
```rust
use yew::Properties;
#[derive(Properties, PartialEq)]
pub struct Props {
pub is_loading: bool,
}
```
## Use in function components
The attribute `#[function_component]` allows to optionally specify Props in the function arguments
<Tabs>
<TabItem value="with-props" label="With Props">
```rust
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} />}
}
```
</TabItem>
<TabItem value="no-props" label="No Props">
```rust
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 />}
}
```
</TabItem>
</Tabs>
## Derive macro field attributes
When deriving `Properties`, all fields are required by default.
The following attributes allow you to give your props initial 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.
:::
<Tabs>
<TabItem value="prop_or_default" label="#[prop_or_default]">
Initialize the prop value with the default value of the field's type using the `Default` trait.
```rust
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} />}
}
```
</TabItem>
<TabItem value="prop_or_value" label="#[prop_or(value)]">
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)]`.
```rust
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()} />}
}
```
</TabItem>
<TabItem value="prop_or_else_function" label="#[prop_or_else(function)]">
Call `function` to initialize the prop value. `function` should have the signature `FnMut() -> T` where `T` is the field type.
```rust
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()} />}
}
```
</TabItem>
</Tabs>
## 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`).
```rust
use yew::{function_component, html, Html, Properties, props};
#[derive(Properties, PartialEq)]
pub struct Props {
#[prop_or("Bob".to_string())]
pub name: String,
}
#[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 />}
}
```

View File

@ -0,0 +1,36 @@
---
title: "Pure Components"
---
## Pure function definition
A function is considered pure when the return values are always identical to passed down parameters. And it has not side-effects.
## Pure components
In the same sense component is pure when it does not have any state or side_effects and with the given props always returns the same `Html`.
For example below is a pure component, for a given prop `is_loading` it will always result in same `Html` without any side effects.
```rust
use yew::{Properties, function_component, Html, html};
#[derive(Properties, PartialEq)]
pub struct Props {
pub is_loading: bool,
}
#[function_component]
fn HelloWorld(props: &Props) -> Html {
if props.is_loading.clone() {
html! { "Loading" }
} else {
html! { "Hello world" }
}
}
```
## Un-pure components
Though you probably wonder how can a component even be un-pure if its just a function that is called every render and if it does not use any globals.
This is where the next topic comes in - [hooks](./hooks)

View File

@ -0,0 +1,17 @@
---
title: "State"
---
## General view of how to store state
This table can be used as a guide when deciding what state storing type fits best for your use case:
| Type | Rerender when? | Scope |
| ---------------------------------------------------- | ---------------------------- | ------------------- |
| [use_state](./hooks/use-state) | got set | component instance |
| [use_state_eq](./hooks/use-state#use_state_eq) | got set with diff. value | component instance |
| [use_reducer](./hooks/use-reducer) | got reduced | component instance |
| [use_reducer_eq](./hooks/use-reducer#use_reducer_eq) | got reduced with diff. value | component instance |
| [use_memo](./hooks/use-memo) | dependencies changed | component instance |
| [use_mut_ref](./hooks/use-mut-ref) | - | component instance |
| a static global variable | - | global, used by all |

View File

@ -133,11 +133,10 @@ fn app() -> Html {
}
```
### Use Suspense in Struct Components
It's not possible to suspend a struct component directly. However, you
can use a function component as a Higher-Order-Component to
can use a function component as a [HOC](../advanced-topics/struct-components/hoc) to
achieve suspense-based data fetching.
```rust ,ignore

View File

@ -1,10 +1,7 @@
---
title: "Project Setup"
sidebar_label: Introduction
description: "Set yourself up for success"
title: "Getting Started"
---
You will need a couple of tools to compile, build, package and debug your Yew application.
When getting started, we recommend using [Trunk](https://trunkrs.dev/). Trunk is a WASM web application
bundler for Rust.

View File

@ -154,5 +154,37 @@
"sidebar.docs.category.yew-router": {
"message": "yew-router",
"description": "The label for category yew-router in sidebar docs"
},
"sidebar.docs.category.Intro With Basic Web Technologies": {
"message": "Intro With Basic Web Technologies",
"description": "The label for category Intro With Basic Web Technologies in sidebar docs"
},
"sidebar.docs.category.Intro With Basic Web Technologies.link.generated-index.title": {
"message": "Yew Take on Basic Web Technologies",
"description": "The generated-index page title for category Intro With Basic Web Technologies in sidebar docs"
},
"sidebar.docs.category.Intro With Basic Web Technologies.link.generated-index.description": {
"message": "Yew mostly operates on the idea of keeping everything that a reusable piece of UI may need, in one place - rust files. But also seeks to stay close to the original look of the technology. Explore further to fully grasp what we mean by these statements:",
"description": "The generated-index page description for category Intro With Basic Web Technologies in sidebar docs"
},
"sidebar.docs.category.Hooks": {
"message": "Hooks",
"description": "The label for category Hooks in sidebar docs"
},
"sidebar.docs.category.Javascript with Rust": {
"message": "Javascript with Rust",
"description": "The label for category Javascript with Rust in sidebar docs"
},
"sidebar.docs.category.Javascript with Rust.link.generated-index.title": {
"message": "wasm-bindgen",
"description": "The generated-index page title for category Javascript with Rust in sidebar docs"
},
"sidebar.docs.category.Javascript with Rust.link.generated-index.description": {
"message": "Learn about wasm-bindgen",
"description": "The generated-index page description for category Javascript with Rust in sidebar docs"
},
"sidebar.docs.category.Struct Components": {
"message": "Struct Components",
"description": "The label for category Struct Components in sidebar docs"
}
}

View File

@ -154,5 +154,37 @@
"sidebar.docs.category.yew-router": {
"message": "yew-router",
"description": "The label for category yew-router in sidebar docs"
},
"sidebar.docs.category.Intro With Basic Web Technologies": {
"message": "Intro With Basic Web Technologies",
"description": "The label for category Intro With Basic Web Technologies in sidebar docs"
},
"sidebar.docs.category.Intro With Basic Web Technologies.link.generated-index.title": {
"message": "Yew Take on Basic Web Technologies",
"description": "The generated-index page title for category Intro With Basic Web Technologies in sidebar docs"
},
"sidebar.docs.category.Intro With Basic Web Technologies.link.generated-index.description": {
"message": "Yew mostly operates on the idea of keeping everything that a reusable piece of UI may need, in one place - rust files. But also seeks to stay close to the original look of the technology. Explore further to fully grasp what we mean by these statements:",
"description": "The generated-index page description for category Intro With Basic Web Technologies in sidebar docs"
},
"sidebar.docs.category.Hooks": {
"message": "Hooks",
"description": "The label for category Hooks in sidebar docs"
},
"sidebar.docs.category.Javascript with Rust": {
"message": "Javascript with Rust",
"description": "The label for category Javascript with Rust in sidebar docs"
},
"sidebar.docs.category.Javascript with Rust.link.generated-index.title": {
"message": "wasm-bindgen",
"description": "The generated-index page title for category Javascript with Rust in sidebar docs"
},
"sidebar.docs.category.Javascript with Rust.link.generated-index.description": {
"message": "Learn about wasm-bindgen",
"description": "The generated-index page description for category Javascript with Rust in sidebar docs"
},
"sidebar.docs.category.Struct Components": {
"message": "Struct Components",
"description": "The label for category Struct Components in sidebar docs"
}
}

View File

@ -154,5 +154,37 @@
"sidebar.docs.category.yew-router": {
"message": "yew-router",
"description": "The label for category yew-router in sidebar docs"
},
"sidebar.docs.category.Intro With Basic Web Technologies": {
"message": "Intro With Basic Web Technologies",
"description": "The label for category Intro With Basic Web Technologies in sidebar docs"
},
"sidebar.docs.category.Intro With Basic Web Technologies.link.generated-index.title": {
"message": "Yew Take on Basic Web Technologies",
"description": "The generated-index page title for category Intro With Basic Web Technologies in sidebar docs"
},
"sidebar.docs.category.Intro With Basic Web Technologies.link.generated-index.description": {
"message": "Yew mostly operates on the idea of keeping everything that a reusable piece of UI may need, in one place - rust files. But also seeks to stay close to the original look of the technology. Explore further to fully grasp what we mean by these statements:",
"description": "The generated-index page description for category Intro With Basic Web Technologies in sidebar docs"
},
"sidebar.docs.category.Hooks": {
"message": "Hooks",
"description": "The label for category Hooks in sidebar docs"
},
"sidebar.docs.category.Javascript with Rust": {
"message": "Javascript with Rust",
"description": "The label for category Javascript with Rust in sidebar docs"
},
"sidebar.docs.category.Javascript with Rust.link.generated-index.title": {
"message": "wasm-bindgen",
"description": "The generated-index page title for category Javascript with Rust in sidebar docs"
},
"sidebar.docs.category.Javascript with Rust.link.generated-index.description": {
"message": "Learn about wasm-bindgen",
"description": "The generated-index page description for category Javascript with Rust in sidebar docs"
},
"sidebar.docs.category.Struct Components": {
"message": "Struct Components",
"description": "The label for category Struct Components in sidebar docs"
}
}

View File

@ -30,15 +30,46 @@ module.exports = {
items: [
{
type: "category",
label: "Components",
link: { type: "doc", id: "concepts/components/introduction" },
label: "Intro With Basic Web Technologies",
link: {
type: "generated-index",
title: "Yew Take on Basic Web Technologies",
description: "Yew mostly operates on the idea of keeping everything that a reusable piece of UI may need, in one place - rust files. But also seeks to stay close to the original look of the technology. Explore further to fully grasp what we mean by these statements:",
},
items: [
"concepts/components/lifecycle",
"concepts/components/scope",
"concepts/components/callbacks",
"concepts/components/properties",
"concepts/components/children",
"concepts/components/refs",
"concepts/basic-web-technologies/html",
"concepts/basic-web-technologies/css",
"concepts/basic-web-technologies/js",
],
},
{
type: "category",
label: "Function Components",
link: { type: "doc", id: "concepts/function-components/introduction" },
items: [
"concepts/function-components/properties",
"concepts/function-components/callbacks",
"concepts/function-components/children",
"concepts/function-components/pure-components",
{
type: "category",
label: "Hooks",
link: { type: "doc", id: "concepts/function-components/hooks/introduction" },
items: [
"concepts/function-components/hooks/use-state",
"concepts/function-components/hooks/use-reducer",
"concepts/function-components/hooks/use-mut-ref",
"concepts/function-components/hooks/use-node-ref",
"concepts/function-components/hooks/use-effect",
"concepts/function-components/hooks/use-memo",
"concepts/function-components/hooks/use-context",
"concepts/function-components/hooks/custom-hooks",
],
},
"concepts/function-components/node-refs",
"concepts/function-components/state",
"concepts/function-components/communication",
"concepts/function-components/generics",
],
},
{
@ -58,17 +89,7 @@ module.exports = {
},
{
type: "category",
label: "Function Components",
items: [
"concepts/function-components/introduction",
"concepts/function-components/attribute",
"concepts/function-components/pre-defined-hooks",
"concepts/function-components/custom-hooks",
],
},
{
type: "category",
label: "wasm-bindgen",
label: "Javascript with Rust",
link: {
type: "generated-index",
title: "wasm-bindgen",
@ -97,6 +118,21 @@ module.exports = {
},
items: [
"advanced-topics/how-it-works",
{
type: "category",
label: "Struct Components",
link: { type: "doc", id: "advanced-topics/struct-components/introduction" },
items: [
"advanced-topics/struct-components/hoc",
"advanced-topics/struct-components/introduction",
"advanced-topics/struct-components/lifecycle",
"advanced-topics/struct-components/scope",
"advanced-topics/struct-components/callbacks",
"advanced-topics/struct-components/properties",
"advanced-topics/struct-components/refs",
],
},
"advanced-topics/children",
"advanced-topics/optimizations",
"advanced-topics/portals",
"advanced-topics/server-side-rendering",
@ -109,12 +145,7 @@ module.exports = {
type: "generated-index",
title: "Miscellaneous",
},
items: [
"more/debugging",
"more/css",
"more/testing",
"more/roadmap",
],
items: ["more/debugging", "more/css", "more/testing", "more/roadmap"],
},
{
type: "category",
@ -138,5 +169,5 @@ module.exports = {
],
},
],
api: [{type: 'autogenerated', dirName: 'tutorial'}],
api: [{ type: "autogenerated", dirName: "tutorial" }],
};