Feliz: Question: Feliz.ElmishComponents vs Feliz.UseElmish

Created on 8 Oct 2020  ·  9Comments  ·  Source: Zaid-Ajaj/Feliz

Hello friend, it's been a while I bothered you with some retarded question, so it's time for another take. 😂

Can you please somehow describe what is the difference between Feliz.ElmishComponents and Feliz.UseElmish? I see them both are React component having Elmish MVU parts. Are there any typical scenarios where you would choose one instead of another? I love easy of use of Feliz.UseElmish and just fear I am maybe missing something even cooler here. 😄

Most helpful comment

Are there any typical scenarios where you would choose one instead of another? I love easy of use of Feliz.UseElmish and just fear I am maybe missing something even cooler here. 😄

Hi Roman, good question!

The difference like Cody mentioned is that Feliz.UseElmish is written with hooks as opposed to a full component implementation of Feliz.ElmishComponents.

However, that is not the entire story, because Feliz.UseElmish also follows _hook semantics_ when it comes to component re-initialization by the use of the dependency array as the last argument which is a big difference from Feliz.ElmishComponents that doesn't do that where reinitialization was hacky: making up unique component keys from the input, yikes!

Feliz.UseElmish can also be combined with other hooks like subscriptions in the same component.

What do I mean by "re-initialization"? Well, imagine you are loading the user profile component based on URL changes as follows:

#/user=profile/{userId} -> UserProfile { UserId = userId }

Basically, we want to re-initialize the UserProfile component (calling init to reset the state) when the input UserId changes:

type UserProfileProps = { UserId : int }

let UserProfile = React.functionComponent("UserProfile", fun (props: UserProfileProps) -> 
  let state, dispatch = React.useElmish(init props.UserId, update, [| props.UserId |])
  Html.h1 (sprintf "UserProfile(UserId=%d)" props.UserId)
)

// later on application  entry: 

React.router [
  router.onUrlChange (parseUrl >> UrlChanged >> dispatch) 
  router.children [
     match state.CurrentUrl with
     | [ "user-profile"; Route.Int userId ] -> UserProfile { UserId = userId }
     | _ -> Html.h1 "Not found" 
  ]
]

Because we providing the the dependency array [| props.UserId |] to the hook (third argument to the hook), the component will automatically refresh and call init again to reset the Elmish dispatch loop. This is what we want because init was a function of the input props.UserId so we want to call it again when the props of the components changes.

When neither the init nor the update function require additional input from the props of the component, you simply provide an empty dependency array:

let state, dispatch = React.useElmish(init, update, [| |])

This is typically used for components that don't need input from outside, like the entry component of the application.

It can be a bit tricky to get used to the dependency array but they make a lot of sense, read more about it in Conditionally firing an effect from the hook docs.

Final tip: Feliz.UseElmish is really powerful because it gives you a full-fledged Elmish dispatch loop the same way you are used to with traditional Elmish apps. However, setting up types for the state and messages can be a bit too much when your components are simple. Do you need a couple of state flags? Then a couple of state variables with React.useState might be enough. Do you need to fetch and render some data without further interaction? Then React.useDeferred might do the trick. I will go for Feliz.UseElmish for complicated pages which have many events and lots of use interaction.

All 9 comments

So Feliz.ElmishComponents isn't very useful anymore, and predates Feliz.UseElmish. The reason for this is that originally Feliz.ElmishComponents was a class based component, which I rewrote as a function component. A bit after that, we realized that if it's a function component it's trivial to just convert it into a hook. That is how Feliz.UseElmish came to be. So now Feliz.ElmishComponents is really just a function component that calls the hook. In fact, if you look at the project for Feliz.ElmishComponents you'll see it's tiny now.

Thanks a lot, Cody!!! So if I get i correctly, Feliz.UseElmish is the default one, right now.

Yeah, we mostly still have Feliz.ElmishComponents around to not break people. You can consider it to be deprecated.

Perfect! Thanks again!

If it is "deprecated" it could be a good idea to mark it "Obselete" and redirect people to the recommended API.

What do you think?

Yes, we are just discussing with colleagues from F# team that we should do PR to documentation (maybe with link to this issue)

Are there any typical scenarios where you would choose one instead of another? I love easy of use of Feliz.UseElmish and just fear I am maybe missing something even cooler here. 😄

Hi Roman, good question!

The difference like Cody mentioned is that Feliz.UseElmish is written with hooks as opposed to a full component implementation of Feliz.ElmishComponents.

However, that is not the entire story, because Feliz.UseElmish also follows _hook semantics_ when it comes to component re-initialization by the use of the dependency array as the last argument which is a big difference from Feliz.ElmishComponents that doesn't do that where reinitialization was hacky: making up unique component keys from the input, yikes!

Feliz.UseElmish can also be combined with other hooks like subscriptions in the same component.

What do I mean by "re-initialization"? Well, imagine you are loading the user profile component based on URL changes as follows:

#/user=profile/{userId} -> UserProfile { UserId = userId }

Basically, we want to re-initialize the UserProfile component (calling init to reset the state) when the input UserId changes:

type UserProfileProps = { UserId : int }

let UserProfile = React.functionComponent("UserProfile", fun (props: UserProfileProps) -> 
  let state, dispatch = React.useElmish(init props.UserId, update, [| props.UserId |])
  Html.h1 (sprintf "UserProfile(UserId=%d)" props.UserId)
)

// later on application  entry: 

React.router [
  router.onUrlChange (parseUrl >> UrlChanged >> dispatch) 
  router.children [
     match state.CurrentUrl with
     | [ "user-profile"; Route.Int userId ] -> UserProfile { UserId = userId }
     | _ -> Html.h1 "Not found" 
  ]
]

Because we providing the the dependency array [| props.UserId |] to the hook (third argument to the hook), the component will automatically refresh and call init again to reset the Elmish dispatch loop. This is what we want because init was a function of the input props.UserId so we want to call it again when the props of the components changes.

When neither the init nor the update function require additional input from the props of the component, you simply provide an empty dependency array:

let state, dispatch = React.useElmish(init, update, [| |])

This is typically used for components that don't need input from outside, like the entry component of the application.

It can be a bit tricky to get used to the dependency array but they make a lot of sense, read more about it in Conditionally firing an effect from the hook docs.

Final tip: Feliz.UseElmish is really powerful because it gives you a full-fledged Elmish dispatch loop the same way you are used to with traditional Elmish apps. However, setting up types for the state and messages can be a bit too much when your components are simple. Do you need a couple of state flags? Then a couple of state variables with React.useState might be enough. Do you need to fetch and render some data without further interaction? Then React.useDeferred might do the trick. I will go for Feliz.UseElmish for complicated pages which have many events and lots of use interaction.

Wow, typical Zaid's response. 😄 You should start teaching those things, man! I can finally understand. Thanks again! ❤️

Was this page helpful?
0 / 5 - 0 ratings