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
    let mut state = use_signal(|| 0); 

    // Because signals are copy, we can move them into closures
    let onclick = move |_| {
        // Signals provide some shortcuts for certain types
        state += 1;
        // But we could do as well
        *state.write() += 1;
    };

    // You can subscribe to a signal, by calling it (`signal()`), 
    // calling the `signal.read()` method, or just embedding it into the RSX.
    // Everytime the signal is mutated the component function will rerun
    // because it has been subscribed, and thus producing a 
    // new Element with the updated counter.
    println!("{}", state());

    rsx!(
        label {
            onclick,
            "State is {value}"
        }
    )
}

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.