YEW Tutorial: 07 Custom like a bike
Tags: rust, yew, webassembly, beginners
Published: 2020-06-15 13:02:56

(Photo by Harley-Davidson on Unsplash)

In this seventh part we are going to see how to update to the new version of yew; then again we will how to write and use custom components using _yewtil_.
Then... No spoiler!

### Code to follow this tutorial

The code has been tagged with the relative tutorial and part.

```Bash
git clone https://github.com/davidedelpapa/yew-tutorial.git
cd yew-tutorial
git checkout tags/v7p1
```

## Part 1: New Yew, and Again on Pure Components

As I was explaining in the [last tutorial](https://github.com/davidedelpapa/yew-tutorial/wiki/Tut-06), the latest versions of Yew, have modify a little the way we should writ our code, namely it became mandatory to explicitly use the `fn change()`, that we saw too in the last article, and explain, even when it should just return `false`.
Moreover, _yewtil_ was purged of dead code and moved into Yew's workspace.

We will proceed with order to update the code we already have from the end of last tutorial.

### Cargo.toml

The versions in _crates.io_ are not fully updated, so here I'll opt for the latest available code in the Github repo:

```toml
yew = { git = "https://github.com/yewstack/yew", features = ["web_sys"] }
yewtil = { git = "https://github.com/yewstack/yew" }
```

You can see that _yewtil_ points to the same repo as _yew_: this is because now they are in the same workspace. **Beginners notice**: this is a somewhat obscure and undocumented way of referring to the crate; I first encountered it using [Diesel](diesel.rs) from git, and it was strange also for me that it should work this way, especially because it is undocumented: there are lots of docs about how to setup a workspace, but not many on how to use two crates from the same workspace, while using the git repo. So beware.

### src/components/button.rs

No changes have to be done. Yup, our code works fine through versions (it is also fairly simple).

### src/app.rs

This is _THE_ change to be done: we have to add a _fn change()_ if we want to correctly impl _Component_ for our _App_ struct, even though there are no _Properties_ (our App, as the entry-point does not need any), so there is nothing to `change`. Well, we'll stub it out and simply return `false`:

```rust
    fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
        App {
            link: link,
            counter: 0,
        }
    }
```

Before all this was simply implied.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/5r83xygxbk7embsarp8z.jpg)

That's it, our app runs as expected!

## Part 2: Improve some more with yewtil

#### Code to follow this part

```Bash
git checkout tags/v7p2
```

We will see presently how to simplify some more the custom components through what are called **function components**. To understand what they are, we can recall that _pure components_ are components that do not need to manipulate directly the state, but update themselves merely if the state changed, on the basis on a comparison between the current and the new state. _Function components_ likewise, can be used for components that do not need to manipulate or act directly in response to state change (presentation components). In addition to _pure components_, _function components_ do not define the state as a struct, and a struct implementation. the state they receive is based on the **function parameters**, that is, the parameters the function defines are effectively those used as state.

An example should suffice, we shall change our trusty _Button_

### src/components/button.rs

We start with the imports:

```rust
use yew::{html, Callback, Html, MouseEvent};
use yewtil::function_component;
```

Previously we were using Yew's prelude only to import `Html`, so now we dispensed with it, and are importing straight `Html`.

From Yewtil we are importing `function_component`.

```rust
#[function_component(Button)]
pub fn button(onsignal: &Callback<MouseEvent>, #[prop_or_default] title: String) -> Html {
    html! {
        <button onclick=onsignal>{ title }</button>
    }
}
```

This is all that is needed! Let's proceed with order:

- we use the directive `#[function_component(<ComponentName>)]`: we define the component name, capitalized as any struct (under the hood in any case a component with its proper struct is created, of course)
- we define a function, and we declare the state we want to use in the parameters of that function (as a good practice it is sufficient the name of the component, not capitalized)
- the function must behave as a `view` function, but beware that there is not a `self` with the state, instead, we have to use the _function parameters_
- in the parameters we can use our trusty `#[prop_or_default]` directive, as we do in the state struct definition.

And it really is the end of it.

What is done under the hood is that a component is created, with a struct that matches our parameters, initialized exactly as a pure component; the `fn view()` of that struct will call the function we defined matching the correct parameters.

It is a really a useful ergonomics for simple presentational components.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/5r83xygxbk7embsarp8z.jpg)

As we can see, nothing really has changed. In this case, it is good news.

## Part 3: improved API ergonomics

Yewtil allows also for an improved way of fetching web API data.

#### Code to follow this part

```Bash
git checkout tags/v7p3
```

### Cargo.toml

Let's bring back [serde](https://serde.rs/), and add the needed feature to yewtil

```toml
[dependencies]
wasm-bindgen = "^0.2"
serde="1"
yew = { git = "https://github.com/yewstack/yew", features = ["web_sys"] }
yewtil = { git = "https://github.com/yewstack/yew", features = ["fetch"] }
```

In fact, we need the `fetch` feature to fetch API data in an improved way.

#### EDIT: July 29, 2020

While trying again to compile today this code, I discovered a bug introduced few hours ago in `wasm-bindgen` that clashes with `gloo-events` bringing down the whole dependency tree! It was introduced in the version 0.2.66 released yesterday!

Meanwhile Yew has updated to version `0.17.2`, and while on the surface everything compiles fine, I haven't had yet the time to explore in depth all the changes.

Thus I recommend using the following dependencies:

```toml
wasm-bindgen = "=0.2.65"
serde="1"
yew = { git = "https://github.com/yewstack/yew", features = ["web_sys"], tag = "0.17.2" }
yewtil = { git = "https://github.com/yewstack/yew", features = ["all", "yewtil-macro"], tag = "0.17.2" }
```

If you want to use the version I used at the time this article was written, then substitute for both `yew` and `yewtil` the `tag="0.17.2"` with `tag="0.16.2"`.

After applying these change I suggest a `cargo clean`, followed by the usual `./run.sh`, that will clean all, download all dependencies, and compile them again. 

### src/api.rs

We add this new file _src/api.rs_. Let's go quickly over the `use` section:

```rust
use serde::{Deserialize, Serialize};
use yewtil::fetch::{FetchRequest, Json, MethodBody};
```

We use serde, as well as some parts of the `yewtil::fetch` mod.

Next we need to replicate the data structures returned by our API. We'll use for this purpose the dummy API `/employees` endpoint, by [restapiexample.com](restapiexample.com); so, we define it in a constant:

```rust
const API_URL: &'static str = "http://dummy.restapiexample.com/api/v1/employees";
```

This is the result of that endpoint call:

```json
{
  "status": "success",
  "data": [
    {
      "id": "1",
      "employee_name": "Tiger Nixon",
      "employee_salary": "320800",
      "employee_age": "61",
      "profile_image": ""
    }
    // .... there are 23 such records in the array
  ]
}
```

Thus we need a struct to hold the JSON object wrapper, with the two fields, `status` and `data`, plus a struct to represent each employee record in the `data` array.

```rust
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct Employee {
    pub id: String,
    pub employee_name: String,
    pub employee_salary: String,
    pub employee_age: String,
    pub profile_image: String,
}
```

This covers the `Employee` records; notice that besides the usual derives, and the `Serialize` / `Deserialize` for serde, we have as well a not too common `Default`. Since we need to init the data structures easily with `Default::default()`, we need to make Rust to derive it for us. If the structures are more complex we explicitly need to impl a `Default` for our structs in order to init them more easily.

The wrapping structure is easy as well:

```rust
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct ApiResponse {
    pub status: String,
    pub data: Vec<Employee>,
}
```

Notice how we derived again the `Default`.

We made both struct completely `pub`, because we need to import them for use in other parts of our project.

Next we need to define a `Request` struct, and we need to impl the `FetchRequest` trait for our `Request` struct:

```rust
#[derive(Default, Debug, Clone)]
pub struct Request;

impl FetchRequest for Request {
    type RequestBody = ();
    type ResponseBody = ApiResponse;
    type Format = Json;

    fn url(&self) -> String {
        API_URL.to_string()
    }

    fn method(&self) -> MethodBody<Self::RequestBody> {
        MethodBody::Get
    }

    fn headers(&self) -> Vec<(String, String)> {
        vec![]
    }

    fn use_cors(&self) -> bool {
        true
    }
}
```

Lots of lines, but the code is very simple to understand.

We have 3 types:

- `RequestBody` if we need to send a body for the request: think of a POST request that wants to add a new employee to the database, and does it with data encoded in the BODY.
- `ResponseBody` the data type onto which to map the response. Beware that some API respond with empty objects sometimes, so be prepared to map for empty objects or nothing, maybe with an `Option`.
- `Format` this type corresponds to the format used for querying and response; in this case we used `Json` from the same `yewtil::fetch`, which re-exports `serde_json`; ideally a type that can default to `serde_json`. Most API in the wild do use JSON, so it is very useful.

Next we define the needed 4 functions:

- `fn url(&self)` has to return a string containing the URL endpoint to poll.

- `fn method(&self)` has to return a `MethodBody` that represents the API request method:

  - `MethodBody::Get` for a "GET" request
  - `MethodBody::Delete` for a "DELETE" request
  - `MethodBody::Post()` for a "POST" request
  - `MethodBody::Put()` for a "PUT" request
  - `MethodBody::Patch()` for a "PATCH" request
  - `MethodBody::Head` for a "HEAD" request

- `fn headers(&self)` has to return a vec of tuples containing 2 String, to be parsed as `(KEY: VALUE)` for the headers; for example:

```rust
vec![
    ("User-Agent".to_string(), "Mozilla/5.0 (Windows NT 10.0; Win64; x64)".to_string())
]
```

- `fn use_cors(&self)` has to return a bool on whether or not to use CORS: it defaults to return `false`, so beware when not implementing it.

We covered all needed in the API fetch part; we will see now how to handle the fetch states and we will create a component in order to take advantage of the API.

### src/components/mod.rs

Since we will implement a custom component, called `Employees` to display the results of the API call, we need to declare its containing .rs file in the _components/mod.rs_, in order to use it as a `mod`:

```rust
pub mod api;
pub mod button;
pub mod employees;
```

We published also `api` so that we can use both the fetch interface and its data structures (notably the `Employee` records)

### src/app.rs

Since there will be just a small addition, we will do it right away: we will add our custom component:

```rust
use crate::components::employees::Employees;
```

This is to `use` it.

```rust
    fn view(&self) -> Html {
        html! {
            <div>
                <h1>{ "Welcome to Components" }</h1>
                <p>{ self.counter } </p>
                <Button onsignal=self.link.callback(|_| Msg::RemoveOne) title="-1" />
                <Button onsignal=self.link.callback(|_| Msg::AddOne) title="+1" />
                <Employees />
            </div>
        }
    }
```

And this is to use it in the interface. Yes, we just simply added the component `<Employees />`

### src/components/employees.rs

Now, for the meatiest part:

```rust
use crate::components::api::{ApiResponse, Employee, Request};
use yew::{html, Component, ComponentLink, Html, ShouldRender};
use yewtil::fetch::{Fetch, FetchAction, FetchState};
use yewtil::future::LinkFuture;
```

It is quite some stuff. Let's proceed with order:

- the `components::api` use, of course.
- next, the `yew` parts needed for implementing a custom component (a regular one in this case)
- then, of course, `yewtil` 's `fetch` parts; notice how we need to handle the state of the request, using `FetchState`.
- lastly `yewtil::future::LinkFuture` is a trait needed for `ComponentLink` to implement a `send_future` action implicitly; so we will not see it in action, yet we need it in scope.

Next we define the struct for our component

```rust
pub struct Employees {
    api: Fetch<Request, ApiResponse>,
    link: ComponentLink<Self>,
}
```

We have the usual `ComponentLink` (to which it is magically added the `send_future` method, just by importing the `future::LinkFuture` of yewtil). We also have a `Fetch` that we aptly called `api`, to which we passed the data to be mapped, `ApiResponse`, and `Request`, our implementation of `FetchRequest`.

We will use a two messages system, as we did previously in the regular `API` polling:

```rust
pub enum Msg {
    SetApiFetchState(FetchAction<ApiResponse>),
    GetApi,
}
```

The `Msg::SetApiFetchState` will be set with the appropriate `FetchAction` state, according to the state of the transaction (do you remember the web API calling model?); the `GetApi` of course is the one that will initiate all, maybe to be attached to a button that the user can press to explicitly send the request.

Now we need to impl the `Component` trait for our `Employees`, as usual, to make it into a custom component:

```rust
impl Component for Employees {
    type Message = Msg;
    type Properties = ();

    fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
        Employees {
            api: Default::default(),
            link,
        }
    }
```

As you can see, in the init of the `api` we use the `std::Default::default()`, that is why we needed Derive it in the data structures of _api.rs_ .

Nothing new nor difficult in the `type` declarations and `vreate` function. Notice, though, that we do not need props, just as we do with `App`, because the state will be internally managed by the component, as if it were a totally independent unit.

The `update` function will handle the Msg's:

```rust
    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::SetApiFetchState(fetch_state) => {
                self.api.apply(fetch_state);
                true
            }
            Msg::GetApi => {
                self.link.send_future(self.api.fetch(Msg::SetApiFetchState));
                self.link
                    .send_message(Msg::SetApiFetchState(FetchAction::Fetching));
                false
            }
        }
    }
```

- The `Msg::SetApiFetchState` is used to communicate the state to the `api` which is the `Fetch` action.
- The `Msg::GetApi` is used to init the API call, so it first uses the `send_future` method we attached to the `ComponentLink`, passing to it the `api` state, and a way to callback once finished fetching; we recall that the first state the `api` is in, is the Default of everything. The default for the state is `FetchAction::NotFetching`, which indicates that it is not fetching right now, nor any other state. After this it uses the method `send_message` (added as well) telling the action to start fetching, with `FetchAction::Fetching`. As soon as it is called to fetch, the component does not need to be re-drawn, that is why we return `false`

Just for reference, these are the 4 possible state of the `Fetch`:

1. `FetchAction::NotFetching`, when it is in a resting state
2. `FetchAction::Fetching`, when it has been initialized and prompted to fetch
3. `FetchAction::Success(result)`, when the fetching succeeded; it wraps the response
4. `FetchAction::Failed(error)`, when the fetching has had some error; it wraps such error.

To them, there corresponds a `FetchState`, thus

1. `FetchState::NotFetching`
2. `FetchState::Fetching`
3. `FetchState::Fetched(result)`
4. `FetchState::Failed(error)`

As already explained, we need to implement the `change` function, even though we do not have props passed by the parent, so it is a simple `ShouldRender=false`

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

Next we will analyze the `view` function:

```rust
    fn view(&self) -> Html {
        match self.api.as_ref().state() {
            FetchState::NotFetching(_) => {
                html! {<button onclick=self.link.callback(|_| Msg::GetApi)>{"Get employees"}</button>}
            }
            FetchState::Fetching(_) => html! {"Fetching"},
            FetchState::Fetched(data) => {
                if data.status != "success".to_string() {
                    return html! {"Api Error!"};
                }
                data.data
                    .iter()
                    .map(|e: &Employee| {
                        html! {
                            <div>
                                <div>
                                    <p>
                                        {"Name: "}
                                        {&e.employee_name}
                                    </p>
                                    <p>
                                        {"Salary: "}
                                        {&e.employee_salary}
                                    </p>
                                    <p>
                                        {"Age: "}
                                        {&e.employee_age}
                                    </p>
                                </div>
                                <hr />
                            </div>
                        }
                    })
                    .collect()
            }
            FetchState::Failed(_, err) => html! {&err},
        }
    }
}
```

The biggest part of the code is taken by a closure that displays the record data present in the response. We will proceed with order:

The function contains a single `match` over `self.api.as_ref().state()` that gets the state of the `api`. We will analyze the 4 state matched:

1. `FetchState::NotFetching`: in this case we return the `html!` containing the button to push, with a callback to `Msg::GetApi`, in order to start fetching the records form the API endpoint. As we can recall from the code above, this messages sends right away the message `SetApiFetchState` wrapping the action `FetchAction::Fetching`. `Msg::SetApiFetchState` when matched in the `update` logic will pass this same action to the `Fetch` interface, that in our struct is called `api`, and then re-draws the component, bringing us to the following branch of our `match`
2. `FetchState::Fetching`: now that we are on this branch we will write a message that we are fetching, instead of the button. With a little of trickery, and the help of a CSS library, we could show a spin animation instead...
3. `FetchState::Fetched(data)`. The first line in the `Msg::GetApi` had set to wrap the response in `Msg::SetApiFetchState` by using the _ComponentLink_'s `send_future` newly acquired method. So once the fetching is ended it either calls `SetApiFetchState` with a `Success`, wrapping a response, or a `Failed`, wrapping the error. In this branch, we match the `FetchState::Fetched`, that ensues, so we take the response, called `data`, and we first check the "status". It might seem confusing, but the call to the API may succeed or fail, and this depends mostly on the connection, if it's disconnected for example. But even if the fetching succeeds, so we have a response from the server, the server can announce to us that it failed to retrieve the data, or because we do not have the necessary authorization (this is not the case with this dummy API). However, in this case the fetching succeeded, because we got a response. But the content of the response might be that the server failed to produce the requested data. So the first thing, as soon as we have a successful answer from the server, is to check if this answer has got the data we need or not. In this case we need only to check that the `status` field is equal to `"success"`.
   In case it is, we can show the content of the records, that we iterate over with a `.iter()` method on the vec of _Employee_, and we map to a closure that just creates html to show the fields of each record (separated by a horizontal rule, `<hr />`). We `clollect` the vector to show it in the component.
   In case the response is a failure, we just return a message of `"Api Error!"`.
4. `FetchState::Failed(error)`: in case the connection failed, we promt the error, with a `&err`, nothing more.

Time to run our app:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/fsbxwvpzamb957illqc0.jpg)

Now we have a button to start fetching.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/85yp6972ovqimpg0bn92.jpg)

This is the message that appears while fetching.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/0svshiv6bj8ze1rpbbx9.jpg)

This the result of our fetching.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/uot6v8izqxrlmwodpsoz.jpg)

I created an error by disconnecting the Internet, so I got an error _Could not create a fetch future_.

## Conclusions

We covered more stuff than I had originally had planned to do, so this seventh installment really had to be added to cover the new `yewtil` interface. I had planned to cover some practical example, but I have to postpone it to the following tutorial. So stay excited: we'll finally cover practical applications starting from next article!