Components

As mentioned at the start of the chapter, Async UI does not have any specific interface that "components" have to conform to. A "component" is just some piece of code that can be used to render UI.

Here are two common forms of "component"

Async Functions

This code from the previous subchapter is somewhat complicated.

#![allow(unused)]
fn main() {
async fn lots_of_span() {
    Div::new() // the wrapping <div>
        .render(join((
            // the <button> at the top
            Button::new().render("Hello World".render()),
            // the 100 <span>s, made by joining a vec of 100 Futures
            join(
                (1..=100)
                    .map(|number| Span::new().render(number.to_string().render()))
                    .collect::<Vec<_>>(),
            ),
            // the <input> at the end
            Input::new().render(),
        )))
        .await;
}
}

We should split it into smaller parts that are easier to understand.

To do this, we'll isolate two of the Futures into a separate async functions (remember that Rust async functions return Future objects).

#![allow(unused)]
fn main() {
async fn hundred_spans() {
    join((1..=100).map(one_span).collect::<Vec<_>>()).await;
}
async fn one_span(number: i32) {
    Span::new().render(number.to_string().render()).await;
}
}

Our overall UI function is now just

#![allow(unused)]
fn main() {
pub async fn lots_of_span_2() {
    Div::new()
        .render(join((
            Button::new().render("Hello World".render()),
            hundred_spans(),
            Input::new().render(),
        )))
        .await;
}
}

Observe that the components we made are just plain Rust async function. Component functions can do all the things regular async functions can do: take arguments, borrow things, be generic, etc.

Types with Async .render() Method

Components can also come in the form of a type.

#![allow(unused)]
fn main() {
struct HelloWorld;
impl HelloWorld {
    async fn render(&self) {
        "Hello World".render().await;
    }
}
async fn app() {
    let hello_world = HelloWorld;
    hello_world.render().await;
}
}

Calling the .render() method returns a Future. Running that Future puts the UI on the screen.

You've seen this before! all the HTML components we've worked with - Div, Input, Button, etc. - are types with .render(_) method.

How is this more useful than plain asnyc functions? It allows us to modify the element, as we'll see in the next chapter...