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
534 stars 78 forks source link

Feliz.UseElmish issue #541

Closed Hardt-Coded closed 1 year ago

Hardt-Coded commented 1 year ago

Hi.

We are injecting via useElmish funtions into the init function. In verison 1.6 of Feliz.UseElmish this worked fine. But with the new version this doesn't work any more.

I can work around this and partially apply the function and create a new "init"-function which doesn't take any function as input at all, but I thought to mention this here, when someone stumple uppon this error:

Example:


// given

let init (customer, errorCallback) =
    ...

let CustomerView (customer:Customer, (doSomething: string -> unit)) = 
    let state,dispatch = React.useEmlish(init, update, (customer, doSomething), [| customer |])
    ...

Workaround:


// given

let init (customer, errorCallback) =
    ...

let CustomerView (customer:Customer, (doSomething: string -> unit)) = 
    let init customer = init customer doSomething   // <- create a new init
    let state,dispatch = React.useEmlish(init, update, customer, [| customer |])  // here use only customer as 3th parameter
    ...

So the error is:

FS0001 The type '(string -> unit)' does not support the 'equality' constraint because it is a function type

Btw is there an more "elegant ways" to inject behavior from upstream components to downstream components? Probably JS-Events or something.

Nevertheless, keep up the awesome work :D

So long

Daniel

Zaid-Ajaj commented 1 year ago

Hi @DieselMeister indeed in the new version of Feliz.UseElmish, we actually need to check whether args have changed to re-init the component so that means that args need to support equality which functions don't do. Unless we stop checking args I am inclined to say that your workaround is by design. That said, it be simplified a bit by making init take unit as input:

[<ReactComponent>]
let CustomerView (customer:Customer, (doSomething: string -> unit)) = 
    let init() = init customer doSomething   // <- create a new init
    let state,dispatch = React.useEmlish(init, update, [| customer |]) 
    ...

Btw is there an more "elegant ways" to inject behavior from upstream components to downstream components? Probably JS-Events or something.

React context comes to mind where it allows you to implicitly propagate data down the component tree. It might look something like this:

[<ReactComponent>]
let CustomerView (customer:Customer) =
    let errorCallback = React.useContext(errorCallbackContext) 
    let init() = init customer errorCallback 
    let state,dispatch = React.useEmlish(init, update, [| customer |]) 
    ...

Though I don't know if that works with your current use case 😄

Nevertheless, keep up the awesome work :D

Thank you! 🙏

Hardt-Coded commented 1 year ago

Thank you for the hints :D

Havbe a nice weekend :D

Zaid-Ajaj commented 1 year ago

Have a nice weekend :D

You too!