Basic components
Components are the building blocks of avalanche
apps.
They're represented as functions since they're fundamentally meant to be pure:
they take in inputs and describe a UI as their output. They're so fundamental that
this book is largely a component tutorial.
Creating components
Though components are written as functions, avalanche
implements some quality-of-life and performance features
not possible with functions. This requires the use of specialized syntax.
If we call a component that takes no parameters called Comp
, for example, we'll call
it with Comp(self)
. These component calls only work within the bodies of other components, and they return the
View
type.
Passing parameters to components
Most components have parameters, and they're named. For example, in the components below, we pass href
by name. However, the last
parameter in a function definition can be supplied without a name as the last parameter in a call.
For Text
, that's the text
parameter, which takes something convertible to a Cow<'_, str>
,
meaning you can pass it either a String
or a &str
.
For every other avalanche_web
component, the unnamed last parameter is children
, which takes an Into<Vec<View>>
.
Below are two versions of a link component. One passes text
and children
explicitly, while the other does it implicitly, as is idiomatic.
#![allow(unused)] fn main() { use avalanche::{component, View}; use avalanche_web::components::{A, Text}; const FIRST_SITE: &str = "http://info.cern.ch/hypertext/WWW/TheProject.html"; #[component] fn LinkExplicit() -> View { A( self, href = FIRST_SITE, children = [ Text(self, text = "The first website") ] ) } #[component] fn LinkImplicit() -> View { A( self, href = FIRST_SITE, [ Text(self, "The first website") ] ) } }
For avalanche_web
components, it's generally recommended to use implicit last parameters, so we'll be using them from now on in this tutorial.
In third-party components, by convention, you should only use them if there is only one
parameter, like in Text
, or if the last parameter is a child or children of the component.
Receiving parameters and tracking
So far, we've dealt only with components with hard-coded data. However, within most practical components, we want to allow other components to pass them data.
To enable that, you simply need to add parameters to your component function. Here, let's say we want a reusable Link
with its own custom functionality,
and want to allow a custom destination and text to be specified. We can add to
and text
parameters:
#![allow(unused)] fn main() { use avalanche::{component, tracked, View}; use avalanche_web::components::{A, Text}; #[component] fn Link(to: &str, text: &str) -> View { A( self, href = tracked!(to), [ Text(self, tracked!(text)) ] ) } }
Notice that for the href
and implicit text
parameters, we pass tracked!
parameters instead of just passing them by value.
That's because when we say a parameter is, for example, a &str
, we actually receive a Tracked<&str>
. A Tracked
value
wraps an inner value with data on whether it's been updated since last render. avalanche
uses this to allow for efficient re-rendering
of components. Calling tracked!
on a Tracked
value gives us its inner value. Since Text
and A
expect &str
-like values, but
the to
and text
parameters are Tracked<&str>
, we use tracked!()
to get &str
values.
We can then construct Link
inside of other components:
#![allow(unused)] fn main() { use avalanche::{component, tracked, View}; use avalanche_web::components::{Text, Div}; use avalanche_web::components::A; #[component] fn Link(to: &str, text: &str) -> View { A( self, href = tracked!(to), [ Text(self, tracked!(text)) ] ) } #[component] fn Example() -> View { Div(self, [ Link( self, to = "https://example.com", text = "example.com" ), Text(self, " is a domain reserved for use in demos.") ]) } }
When rendered, the above is equivalent to the HTML below.
<div>
<a href="https://example.com">
example.com
</a>
is a domain reserved for use in demos.
</div>
Within component calls, parameter order does not matter (except for what the implict last parameter is),
so we could've also called Link
with to
and text
swapped:
Link(
self,
text = "example.com".into(),
to = "https://example.com".into()
)
Parameter type restrictions
All parameters must implement Clone
. Note that all non-mut
references implement Clone
, making this restriction
generally easy to deal with in practice.