MVU pattern for Dioxus, heavily inspiret by F# Feliz use_elmish hook.
Predictable State management with no Suprises
#[ derive( Clone ) ]
struct Model {
count : i32 ,
}
enum Msg {
ADD ,
SUB ,
RESET ,
}
fn init ( ) -> Model {
Model { count : 0 }
}
fn update ( msg : Msg , state : Model ) -> Model {
match msg {
Msg :: ADD => Model {
count : state. count + 1 ,
} ,
Msg :: SUB => Model {
count : state. count - 1 ,
} ,
Msg :: RESET => Model { count : 0 } ,
}
}
#[ component]
fn Counter ( ) -> Element {
let elm = use_elmish_simple ( init, update) ;
let model = elm. model ( ) ;
rsx ! {
div {
id: "hero" ,
button {
onclick: move |_| { elm. dispatch( Msg :: SUB ) } ,
"-"
}
"{model.count}"
button {
onclick: move |_| { elm. dispatch( Msg :: ADD ) } ,
"+"
}
button {
onclick: move |_| { elm. dispatch( Msg :: RESET ) } ,
"RESET"
}
}
}
}
Simple Representation for async command flows
#[ component]
fn BreedPic ( breed : ReadOnlySignal < String > ) -> Element {
#[ derive( Default , Debug , Clone ) ]
struct Model {
dog : Option < DogApi > ,
error : Option < String >
}
enum Msg {
GettingDog ( String ) ,
GotDog ( DogApi ) ,
GetDogError ( reqwest:: Error ) ,
}
let get_dog = async move |breed : String | -> Result < DogApi , reqwest:: Error > {
reqwest:: get ( format ! ( "https://dog.ceo/api/breed/{breed}/images/random" ) )
. await ?
. json :: < DogApi > ( )
. await
} ;
let update = move |msg : Msg , state : Model | match msg {
Msg :: GettingDog ( breed) => (
Model { dog : None , ..state } ,
Cmd :: of_async_res ( move || get_dog ( breed) , Msg :: GotDog , Msg :: GetDogError ) ,
) ,
Msg :: GotDog ( dog) => ( Model { dog : Some ( dog) , ..state } , Cmd :: none ( ) ) ,
Msg :: GetDogError ( e) => ( Model { dog : None , error : format ! ( "error getting dogs: {e}" ) } , Cmd :: none ( ) ) ,
} ;
let elmish = use_elmish_with_cmd ( Model :: default, update, move || {
Cmd :: of_msg ( Msg :: GettingNewDog ( breed ( ) ) )
} ) ;
let model = elmish. model ( ) ;
if let Some ( dog) = model. dog {
rsx ! {
div {
button { onclick: move |_| elmish. dispatch( Msg :: GettingNewDog ( breed( ) ) ) , padding: "5px" , background_color: "gray" , color: "white" , border_radius: "5px" , "Click to fetch another doggo" }
img { max_width: "500px" , max_height: "500px" , src: "{dog.message}" }
}
}
} else if let Some ( err) = model. error {
rsx ! ( model. error)
} else {
rsx ! ( "loading doggo..." )
}
// This resource will restart whenever the breed changes
}