Learn Dioxus
This is a quick introduction to Dioxus in the world of Freya. For more examples or tips you can check the official Dioxus Docs or the Dioxus Cheatsheet
Components
Components in Dioxus are defined in the form of funtions that might receive some props
and return an Element
.
The Element is generated by the rsx!()
macro.
fn MyComponent() -> Element {
rsx!(...)
}
RSX
Dioxus uses a custom markup syntax called RSX, it's conceptually similar to React's JSX.
Syntax:
<Element Name> {
<Element Attribute Name>: <Element Attribute Value>,
<Element Children>
}
Example:
rsx!(
label {
onclick: |_| println!("Clicked"),
color: "red",
font_size: "50",
"Hello, World!"
}
)
Another Example:
rsx!(
rect {
color: "red",
onclick: |_| println!("Clicked rect"),
label {
onclick: |_| println!("Clicked label"),
font_size: "50",
"Hello, World!"
}
AnotherComponent {
some_value: 123
}
}
)
Props
Use the component
macro if you want to have inlined props:
#[component]
fn MyComponent(name: String, age: u8) -> Element {
rsx!(
label {
"{name} is {age} years old."
}
)
}
You can as well have a separate Struct for the props:
struct MyComponentProps {
name: String,
age: u8
}
fn MyComponent(props: MyComponentProps) -> Element {
rsx!(
label {
"{props.name} is {props.age} years old."
}
)
}
State
Dioxus built-in state management uses Signals, and they are usually created with the use_signal
hook.
fn MyComponent() -> Element {
// `use_signal` takes a callback that initializes the state
// No matter how many times the component re runs,
// the initialization callback will only run once at the first component run
let mut state = use_signal(|| 0);
// Because signals are copy, we can move them into closures
let onclick = move |_| {
// Signals provide some mutation shortcuts for certain types
state += 1;
// But we could do as well
*state.write() += 1;
};
// You subscribe to a signal by calling it (`signal()`),
// calling the `read()` method, or just embedding it into the RSX.
// Everytime the signal is mutated the component function will rerun
// because it has been subscribed, this will end up producing a
// new `Element` with the updated counter.
println!("{}", state());
println!("{}", state.read());
rsx!(
label {
onclick,
"State is {state}"
}
)
}
Shared State
Signals can be passed to other components so they can read/write to the same signal.
fn app() -> Element {
let state = use_signal(|| 0);
// We pass the signal through the context API
// So `ThirdComponent` can consume
use_context_provider(|| state);
rsx!(
SecondComponent {
state // We can pass the signal as a prop as well
}
ThirdComponent {}
)
}
#[component]
fn SecondComponent(mut state: Signal<usize>) -> Element {
let onclick = move |_| {
state += 1;
};
rsx!(
label {
onclick,
"Second component: {state}"
}
)
}
#[component]
fn ThirdComponent() -> Element {
// We consume the signal passed through `use_context_provider`
let mut state = use_context::<Signal<usize>>();
let onclick = move |_| {
state += 1;
};
rsx!(
label {
onclick,
"Third component: {state}"
}
)
}
Alternative State Management
There are other state management libraries with more granular control or with other goals that are worth checking out.