Your First Component
Now that we've initialized our HotDog app, we can start building out its components.
What is a component?
In Dioxus, apps are comprised of individual functions called Components that take in some Properties and render an Element:
fn DogApp(props: DogAppProps) -> Element { // ... }
Component Properties
All components take an object that outlines which parameters the component can accept. All Props
structs in Dioxus need to derive the Properties
trait which requires both Clone
and PartialEq
:
#[derive(Props, PartialEq, Clone)] struct DogAppProps { breed: String }
Dioxus provides the #[component]
macro for simplifying how components are defined. This macro converts the parameters of the annotated function into a hidden accompanying struct.
#[component] fn DogApp(breed: String) -> Element { // ... }
When building apps, you'll frequently use the #[component]
macro. When building libraries, we generally suggest deriving Props instead.
Properties are Immutable
If you're familiar with JavaScript, then you might also be familiar with libraries like React. Dioxus is very similar to React: if you know React then you will feel comfortable with Dioxus.
Just like React, Dioxus components are rendered by calling the function component. On every render, Dioxus makes a .clone()
of the component's props. This ensures you can't accidentally modify your props which can lead to hard-to-track issues with state management.
fn DogApp(props: DogAppProps) -> Element { tracing::info!("Rendered with breed: {}", props.breed); // ... }
Dioxus provides types that make .clone()
cheaper to call, so don't worry about performance gotchas here.
Component Functions are Called Multiple Times
Just like React, Dioxus will call your component function multiple times throughout its lifecycle. This is called re-rendering. In Dioxus, re-renders are extremely cheap (much cheaper than React!). In most cases you shouldn't worry about re-rendering too frequently.
When Dioxus re-renders your component, it compares the Element
returned from the last render against the Element
returned in the current render.
For example, when the breed
property changes on the DogApp component, Dioxus will call the DogApp function a second time and compare the previous Element against the new Element.
#[component] fn DogApp(breed: String) -> Element { rsx! { "Breed: {breed}" } }
Dioxus will re-render your component in only two circumstances:
- When the
Props
change as determined byPartialEq
- When a function like
signal.set()
orsignal.write()
callsScope.needs_update()
Unlike React, all Dioxus components are memoized by default meaning Dioxus will always compare Props
before deciding to re-render your component. As an additional optimization, Dioxus only compares dynamic parts of your RSX. Elements that don't contain dynamic data won't be checked for changes.
Composing Components
In Dioxus, Components are composed together to create Apps. Each component will hold onto its own state and handle its own updates. This makes it easy to abstract your app into different parts and even share pieces of your app as libraries for others to use.
To compose components together, we'll use the rsx! {}
macro to define the structure of our app.
#[component] fn App() -> Element { rsx! { Header {} DogApp { breed: "corgi" } Footer {} } }
We'll cover rsx! {}
in more depth in the next chapter.