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...