Zaid-Ajaj / Feliz

A fresh retake of the React API in Fable and a collection of high-quality components to build React applications in F#, optimized for happiness
https://zaid-ajaj.github.io/Feliz/
MIT License
531 stars 77 forks source link

Making UseElmish's dispatch function stable #588

Closed lukaszkrzywizna closed 7 months ago

lukaszkrzywizna commented 8 months ago

Hi @Zaid-Ajaj! During running my application I've encountered a problem with React.UseElmish. When a component using React.memo with custom equals function (omitting functions), the dispatch provided from UseElmish simply doesn't work. The issue is caused by replacing the initialDispatch function with a real one, provided by Elmish's program. We can simply fix this by making dispatch stable - hook would always reuse initialDispatch but would check its logic when the Program's dispatch is provided. The code below provided the discussed scenario:

type private Model = {
  Counter: int
}

type private Msg =
  | Increase

let private init () = { Counter = 0 }, Cmd.none
let private update msg model =
  match msg with
  | Increase -> { model with Counter = model.Counter + 1 }, Cmd.none

let private MButton =
  React.memo((fun (props: {| counter: int; dispatch: Msg -> unit |}) ->
    Html.button [
      prop.text $"Counter: {props.counter}"
      prop.onClick (fun _ -> props.dispatch Increase)
    ]),
    areEqual = Fable.React.Helpers.equalsButFunctions // this will force to use initial dispatch function
  )

let App() =
  let state, dispatch = React.useElmish(init, update, [||])
  MButton {|
    counter = state.Counter
    dispatch = dispatch
  |}

Note: I did not provided any test for it. I have a problem with running Feliz project and tests. Do you have any manual how to run it?