Introduction

What are Components?

Components are the building blocks of Yew. They manage their own state and can render themselves to the DOM. Components are created by implementing the Component trait for a type. The Component trait has a number of methods which need to be implemented; Yew will call these at different stages in the lifecycle of a component.

Lifecycle

Introduction - 图1contribute

Contribute to our docs: Add a diagram of the component lifecycle

Lifecycle Methods

Create

When a component is created, it receives properties from its parent component and is stored within the Context<Self> thats passed down to the create method. The properties can be used to initialize the component’s state and the “link” can be used to register callbacks or send messages to the component.

  1. use yew::{Component, Context, html, Html, Properties};
  2. #[derive(PartialEq, Properties)]
  3. pub struct Props;
  4. pub struct MyComponent;
  5. impl Component for MyComponent {
  6. type Message = ();
  7. type Properties = Props;
  8. fn create(ctx: &Context<Self>) -> Self {
  9. MyComponent
  10. }
  11. fn view(&self, _ctx: &Context<Self>) -> Html {
  12. html! {
  13. // impl
  14. }
  15. }
  16. }

View

The view method allows you to describe how a component should be rendered to the DOM. Writing HTML-like code using Rust functions can become quite messy, so Yew provides a macro called html! for declaring HTML and SVG nodes (as well as attaching attributes and event listeners to them) and a convenient way to render child components. The macro is somewhat similar to React’s JSX (the differences in programming language aside). One difference is that Yew provides a shorthand syntax for properties, similar to Svelte, where instead of writing onclick={onclick}, you can just write {onclick}.

  1. use yew::{Component, Context, html, Html, Properties};
  2. enum Msg {
  3. Click,
  4. }
  5. #[derive(PartialEq, Properties)]
  6. struct Props {
  7. button_text: String,
  8. }
  9. struct MyComponent;
  10. impl Component for MyComponent {
  11. type Message = Msg;
  12. type Properties = Props;
  13. fn create(_ctx: &Context<Self>) -> Self {
  14. Self
  15. }
  16. fn view(&self, ctx: &Context<Self>) -> Html {
  17. let onclick = ctx.link().callback(|_| Msg::Click);
  18. html! {
  19. <button {onclick}>{ &ctx.props().button_text }</button>
  20. }
  21. }
  22. }

For usage details, check out the html! guide.

Rendered

The rendered component lifecycle method is called once view has been called and Yew has rendered the results to the DOM, but before the browser refreshes the page. This method is useful when you want to perform actions that can only be completed after the component has rendered elements. There is also a parameter called first_render which can be used to determine whether this function is being called on the first render, or instead a subsequent one.

  1. use web_sys::HtmlInputElement;
  2. use yew::{
  3. Component, Context, html, Html, NodeRef,
  4. };
  5. pub struct MyComponent {
  6. node_ref: NodeRef,
  7. }
  8. impl Component for MyComponent {
  9. type Message = ();
  10. type Properties = ();
  11. fn create(_ctx: &Context<Self>) -> Self {
  12. Self {
  13. node_ref: NodeRef::default(),
  14. }
  15. }
  16. fn view(&self, ctx: &Context<Self>) -> Html {
  17. html! {
  18. <input ref={self.node_ref.clone()} type="text" />
  19. }
  20. }
  21. fn rendered(&mut self, _ctx: &Context<Self>, first_render: bool) {
  22. if first_render {
  23. if let Some(input) = self.node_ref.cast::<HtmlInputElement>() {
  24. input.focus();
  25. }
  26. }
  27. }
  28. }

Introduction - 图2note

Note that this lifecycle method does not require an implementation and will do nothing by default.

Update

Communication with components happens primarily through messages which are handled by the update lifecycle method. This allows the component to update itself based on what the message was, and determine if it needs to re-render itself. Messages can be sent by event listeners, child components, Agents, Services, or Futures.

Here’s an example of what an implementation of update could look like:

  1. use yew::{Component, Context, html, Html};
  2. pub enum Msg {
  3. SetInputEnabled(bool)
  4. }
  5. struct MyComponent {
  6. input_enabled: bool,
  7. }
  8. impl Component for MyComponent {
  9. type Message = Msg;
  10. type Properties = ();
  11. fn create(_ctx: &Context<Self>) -> Self {
  12. Self {
  13. input_enabled: false,
  14. }
  15. }
  16. fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
  17. match msg {
  18. Msg::SetInputEnabled(enabled) => {
  19. if self.input_enabled != enabled {
  20. self.input_enabled = enabled;
  21. true // Re-render
  22. } else {
  23. false
  24. }
  25. }
  26. }
  27. }
  28. fn view(&self, _ctx: &Context<Self>) -> Html {
  29. html! {
  30. // impl
  31. }
  32. }
  33. }

Changed

Components may be re-rendered by their parents. When this happens, they could receive new properties and need to re-render. This design facilitates parent to child component communication by just changing the values of a property. There is a default implementation which re-renders the component when props are changed.

Destroy

After Components are unmounted from the DOM, Yew calls the destroy lifecycle method; this is necessary if you need to undertake operations to clean up after earlier actions of a component before it is destroyed. This method is optional and does nothing by default.

Infinite loops

Infinite loops are possible with Yew’s lifecycle methods, but are only caused when trying to update the same component after every render when that update also requests the component to be rendered.

A simple example can be seen below:

  1. use yew::{Context, Component, Html};
  2. struct Comp;
  3. impl Component for Comp {
  4. type Message = ();
  5. type Properties = ();
  6. fn create(_ctx: &Context<Self>) -> Self {
  7. Self
  8. }
  9. fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
  10. // We are going to always request to re-render on any msg
  11. true
  12. }
  13. fn view(&self, _ctx: &Context<Self>) -> Html {
  14. // For this example it doesn't matter what is rendered
  15. Html::default()
  16. }
  17. fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
  18. // Request that the component is updated with this new msg
  19. ctx.link().send_message(());
  20. }
  21. }

Let’s run through what happens here:

  1. Component is created using the create function.
  2. The view method is called so Yew knows what to render to the browser DOM.
  3. The rendered method is called, which schedules an update message using the Context link.
  4. Yew finishes the post-render phase.
  5. Yew checks for scheduled events and sees the update message queue is not empty so works through the messages.
  6. The update method is called which returns true to indicate something has changed and the component needs to re-render.
  7. Jump back to 2.

You can still schedule updates in the rendered method and it’s often useful to do so, but consider how your component will terminate this loop when you do.

Associated Types

The Component trait has two associated types: Message and Properties.

  1. impl Component for MyComponent {
  2. type Message = Msg;
  3. type Properties = Props;
  4. // ...
  5. }

The Message type is used to send messages to a component after an event has taken place; for example you might want to undertake some action when a user clicks a button or scrolls down the page. Because components tend to have to respond to more than one event, the Message type will normally be an enum, where each variant is an event to be handled.

When organizing your codebase, it is sensible to include the definition of the Message type in the same module in which your component is defined. You may find it helpful to adopt a consistent naming convention for message types. One option (though not the only one) is to name the types ComponentNameMsg, e.g. if your component was called Homepage then you might call the type HomepageMsg.

  1. enum Msg {
  2. Click,
  3. FormInput(String)
  4. }

Properties represents the information passed to a component from its parent. This type must implement the Properties trait (usually by deriving it) and can specify whether certain properties are required or optional. This type is used when creating and updating a component. It is common practice to create a struct called Props in your component’s module and use that as the component’s Properties type. It is common to shorten “properties” to “props”. Since props are handed down from parent components, the root component of your application typically has a Properties type of (). If you wish to specify properties for your root component, use the App::mount_with_props method.

Context

All component lifecycle methods take a context object. This object provides a reference to component’s scope, which allows sending messages to a component and the props passed to the component.