Basic Interactions with Yew
Tags: rust, frontend
Published: 2020-11-21 20:18:36

---
title: Basic Interactions with Yew
published: true
tags: rust, frontend
series: Yew
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/x2zc0imz7z63iwfax007.jpg
---

Somehow the last article about Yew got more traction than I anticipated. Not much, but still!

That's why I sat down and played a bit around with Yew again, in the hope that one day a Yew maintainer will jump in and call me out on the Rust/Yew crap I'm teaching people here :D

I still hope to get a bit better at Rust, but I have to admit this framework made most of the things pretty easy. I didn't fight the borrow checker much until now. But I'm still in the "let's sprinkle some &s here and there"-stage without really understanding what's going on, haha.

In this article, we will build components with fundamental interactions. Again, nothing fancy, just a counter and a text-area. They don't even interact with each other.

TL;DR: The finished app [can be found on GitHub](https://github.com/fllstck/yew-interaction).

## Setup

I won't go into installing Rust, wasm_bindgen, or setting up a Yew project. [All that can be found in the previous article](https://dev.to/fllstck/intro-to-yew-a-rust-frontend-framework-20hb).

## Creating the Main Component

Like in the previous article, we are going to have three components. The first one is the actual app and lives in `src/lib.rs`. It has the following code:

```rust
#![recursion_limit="1000"]

mod counter;
use counter::Counter;

mod text;
use text::Text;

use wasm_bindgen::prelude::*;
use yew::prelude::*;

struct Model {}

impl Component for Model {
    type Message = ();
    type Properties = ();

    fn create(_props: Self::Properties, _link: ComponentLink<Self>) -> Self {
        Self {}
    }

    fn update(&mut self, _msg: Self::Message) -> ShouldRender {
        false
    }

    fn change(&mut self, _props: Self::Properties) -> ShouldRender {
        false
    }

    fn view(&self) -> Html {
        html! {
            <div>
                <Counter />
                <Counter />
                <Text />
                <Text />
            </div>
        }
    }
}

#[wasm_bindgen(start)]
pub fn run_app() {
    App::<Model>::new().mount_to_body();
}
```

It doesn't do anything but rendering the other two components we will implement. Compared to the previous article, nothing new here.

## Creating the Counter Component

The counter component displays a number that can be incremented and decremented; the 101 of UI interactions. Create a new file at `src/counter.rs` and add this code:

```rust
use yew::prelude::*;

pub struct Counter {
    link: ComponentLink<Self>,
    value: i64,
}

pub enum Msg {
    Increment,
    Decrement,
}

impl Component for Counter {
    type Message = Msg;
    type Properties = ();

    fn create(_props: Self::Properties, link: ComponentLink<Self>) -> Self {
        Self {
            link,
            value: 0,
        }
    }

    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::Increment => self.value += 1,
            Msg::Decrement => self.value -= 1,
        }
        true
    }

    fn change(&mut self, _props: Self::Properties) -> ShouldRender {
        false
    }

    fn view(&self) -> Html {
        html! {
            <div>
              <button onclick=self.link.callback(|_| Msg::Increment)>{"+"}</button>
              <span style="width: 50px">{ self.value }</span>
              <button onclick=self.link.callback(|_| Msg::Decrement)>{"-"}</button>
            </div>
        }
    }
}
```

First, there is a `struct` that holds our state. It consists of a `link` and a `value`. The `link` is required to execute our component methods later, for example, when a button is clicked. The `value` is the number that can be updated later.

Second, there is an `enum` that defines messages. Messages can be used with the `update` method of our component to tell it what to do. In this case, we have two messages, `Increment` and `Decrement`. 

We have to set the `Message` type of the Component trait to our  `Msg` `enum`, so the type inferences work correctly (I guess?). 

The `create` method sets the `value` to zero when a new component instance is created.

The `update` method is responsible for updating the state. It receives `Increment` or `Decrement` messages and acts accordingly on our `value` state.

The `change` method doesn't do anything because our component doesn't accept any properties from its parent.

The `view` gets called when the `update` method returned a `true`, which it always does. If we had some background state that isn't visible to the user, we could skip a render by returning a `false` from the `update` method.

In the `view` method, we can also see the use of the `link` state. The `callback` method of the `link` receives ... well ... a callback/closure. This callback has to return the message (`Increment` or `Decrement`) that we want to handle in the `update` method from above.

Overall, the whole thing isn't much different from React or Vue, just a bit more code required.

## Creating the Text Component

The text component is a single `textarea` element that listens to its inputs and renders out upper-case versions of the text entered into it.

Create a `src/text.rs` file with this code:

```rust
use yew::prelude::*;

pub struct Text {
    link: ComponentLink<Self>,
    content: String,
}

pub enum Msg {
    Update(String),
}

impl Component for Text {
    type Message = Msg;
    type Properties = ();

    fn create(_props: Self::Properties, link: ComponentLink<Self>) -> Self {
        Self { link, content: "".to_string() }
    }

    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::Update(content) => self.content = content.to_uppercase(),
        }
        true
    }

    fn change(&mut self, _props: Self::Properties) -> ShouldRender {
        false
    }

    fn view(&self) -> Html {
        html! {
            <textarea
                oninput=self.link.callback(|event: InputData| Msg::Update(event.value))
                value=&self.content>
            </textarea>
        }
    }
}
```

This component is mostly the same as the last. The main difference is that it only one message, but this time the message has some data.

In this case, a `String` value. To get the data out of the `textarea` element and into our `Msg::Update` message, we have to look into the `view` method.

The `oninput` callback uses the `event` argument this time. It's of type `InputData` and comes with its fresh `value`. We can call `Msg::Update(event.value)` to wrap our new value with our message, and both will be on their way to the `update` method.

The `update` method uses `match` again to check which message we received. Still, this time it destructures the message so we can use the `content` (previously `value`) to update the state of our component; we also call `to_uppercase` on it, a method of `String`, to make it evident that the new text went through this method. Since we return `true`, the `view` method will be called by the framework after updating and rendering our new text. 

## Summary

You get an update/view loop going that is triggered by you clicking/typing things, same as with JavaScript/React. Since the components are encapsulated, I can do my thing in one component without looking into another.

Sure, Rust is much wordier than JavaScript, but then you get more helpful error messages. Also, enums and pattern matching is a very powerful tool that is missing in JavaScript and I could imagine in the long run worth the extra code to write.

I found this to be a good Rust exercise. I learned that I still doesn't understand the module system, let alone macros.