How to Upgrade to Dioxus 0.5
This guide will outline the API changes between the 0.4
and 0.5
releases.
0.5
has includes significant changes to hooks, props, and global state.
Cheat Sheet
Here is a quick cheat sheet for the changes:
Scope
Dioxus 0.4:
fn app(cx: Scope) -> Element { cx.use_hook(|| { /*...*/ }); cx.provide_context({ /*...*/ }); cx.spawn(async move { /*...*/ }); cx.render(rsx! { /*...*/ }) }
Dioxus 0.5:
src/migration.rs
use dioxus::prelude::*; // In dioxus 0.5, the scope is no longer passed as an argument to the function fn app() -> Element { // Hooks, context, and spawn are now called directly use_hook(|| { /*...*/ }); provide_context({ /*...*/ }); spawn(async move { /*...*/ }); rsx! { /*...*/ } }
Props
Dioxus 0.4:
#[component] fn Comp(cx: Scope, name: String) -> Element { // You pass in an owned prop, but inside the component, it is borrowed (name is the type &String inside the function) let owned_name: String = name.clone(); cx.render(rsx! { "Hello {owned_name}" BorrowedComp { "{name}" } ManualPropsComponent { name: name } }) } #[component] fn BorrowedComp<'a>(cx: Scope<'a>, name: &'a str) -> Element<'a> { cx.render(rsx! { "Hello {name}" }) } #[derive(Props, PartialEq)] struct ManualProps { name: String } fn ManualPropsComponent(cx: Scope<ManualProps>) -> Element { cx.render(rsx! { "Hello {cx.props.name}" }) }
Dioxus 0.5:
src/migration.rs
use dioxus::prelude::*; // In dioxus 0.5, props are always owned. You pass in owned props and you get owned props in the body of the component #[component] fn Comp(name: String) -> Element { // Name is owned here already (name is the type String inside the function) let owned_name: String = name; rsx! { "Hello {owned_name}" BorrowedComp { name: "other name" } ManualPropsComponent { name: "other name 2" } } } // Borrowed props are removed in dioxus 0.5. Mapped signals can act similarly to borrowed props if your props are borrowed from state // ReadOnlySignal is a copy wrapper over a state that will be automatically converted to #[component] fn BorrowedComp(name: ReadOnlySignal<String>) -> Element { rsx! { "Hello {name}" } } // In dioxus 0.5, props need to implement Props, Clone, and PartialEq #[derive(Props, Clone, PartialEq)] struct ManualProps { name: String, } // Functions accept the props directly instead of the scope fn ManualPropsComponent(props: ManualProps) -> Element { rsx! { "Hello {props.name}" } }
You can read more about the new props API in the Props Migration guide.
Futures
Dioxus 0.4:
use_future((dependency1, dependency2,), move |(dependency1, dependency2,)| async move { /*use dependency1 and dependency2*/ });
Dioxus 0.5:
src/migration.rs
// dependency1 and dependency2 must be Signal-like types like Signal, ReadOnlySignal, GlobalSignal, or another Resource use_resource(|| async move { /*use dependency1 and dependency2*/ }); let non_reactive_state = 0; // You can also add non-reactive state to the resource hook with the use_reactive macro use_resource(use_reactive!(|(non_reactive_state,)| async move { tokio::time::sleep(std::time::Duration::from_secs(1)).await; non_reactive_state + 1 }));
Read more about the use_resource
hook in the Hook Migration guide.
State Hooks
Dioxus 0.4:
let copy_state = use_state(cx, || 0); let clone_local_state = use_ref(cx, || String::from("Hello")); use_shared_state_provider(cx, || String::from("Hello")); let clone_shared_state = use_shared_state::<String>(cx); let copy_state_value = **copy_state; let clone_local_state_value = clone_local_state.read(); let clone_shared_state_value = clone_shared_state.read(); cx.render(rsx!{ "{copy_state_value}" "{clone_shared_state_value}" "{clone_local_state_value}" button { onclick: move |_| { copy_state.set(1); *clone_local_state.write() = "World".to_string(); *clone_shared_state.write() = "World".to_string(); }, "Set State" } })
Dioxus 0.5:
src/migration.rs
// You can now use signals for local copy state, local clone state, and shared state with the same API let mut copy_state = use_signal(|| 0); let mut clone_shared_state = use_context_provider(|| Signal::new(String::from("Hello"))); let mut clone_local_state = use_signal(|| String::from("Hello")); // Call the signal like a function to clone the current value let copy_state_value = copy_state(); // Or use the read method to borrow the current value let clone_local_state_value = clone_local_state.read(); let clone_shared_state_value = clone_shared_state.read(); rsx! { "{copy_state_value}" "{clone_shared_state_value}" "{clone_local_state_value}" button { onclick: move |_| { // All three states have the same API for updating the state copy_state.set(1); clone_shared_state.set("World".to_string()); clone_local_state.set("World".to_string()); }, "Set State" } }
Read more about the use_signal
hook in the State Migration guide.
Fermi
Dioxus 0.4:
use dioxus::prelude::*; use fermi::*; static NAME: Atom<String> = Atom(|_| "world".to_string()); fn app(cx: Scope) -> Element { use_init_atom_root(cx); let name = use_read(cx, &NAME); cx.render(rsx! { div { "hello {name}!" } Child {} ChildWithRef {} }) } fn Child(cx: Scope) -> Element { let set_name = use_set(cx, &NAME); cx.render(rsx! { button { onclick: move |_| set_name("dioxus".to_string()), "reset name" } }) } static NAMES: AtomRef<Vec<String>> = AtomRef(|_| vec!["world".to_string()]); fn ChildWithRef(cx: Scope) -> Element { let names = use_atom_ref(cx, &NAMES); cx.render(rsx! { div { ul { names.read().iter().map(|f| rsx!{ li { "hello: {f}" } }) } button { onclick: move |_| { let names = names.clone(); cx.spawn(async move { names.write().push("asd".to_string()); }) }, "Add name" } } }) }
Dioxus 0.5:
src/migration.rs
use dioxus::prelude::*; // Atoms and AtomRefs have been replaced with GlobalSignals static NAME: GlobalSignal<String> = Signal::global(|| "world".to_string()); fn app() -> Element { rsx! { // You can use global state directly without the use_read or use_set hooks div { "hello {NAME}!" } Child {} ChildWithRef {} } } fn Child() -> Element { rsx! { button { onclick: move |_| *NAME.write() = "dioxus".to_string(), "reset name" } } } // Atoms and AtomRefs have been replaced with GlobalSignals static NAMES: GlobalSignal<Vec<String>> = Signal::global(|| vec!["world".to_string()]); fn ChildWithRef() -> Element { rsx! { div { ul { for name in NAMES.read().iter() { li { "hello: {name}" } } } button { onclick: move |_| { // No need to clone the signal into futures, you can use it directly async move { NAMES.write().push("asd".to_string()); } }, "Add name" } } } }
You can read more about global signals in the Fermi migration guide.