Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FunctionComponent.Of with generics doesn't call memoizeWith #162

Open
leolorenzoluis opened this issue May 20, 2019 · 1 comment
Open

FunctionComponent.Of with generics doesn't call memoizeWith #162

leolorenzoluis opened this issue May 20, 2019 · 1 comment

Comments

@leolorenzoluis
Copy link
Member

leolorenzoluis commented May 20, 2019

Given I have the following code

let test<'T> =
  FunctionComponent.Of((fun (props: {|Dispatch: Msg -> unit; test: 'T|}) ->
        log props.test
        span [] []
          ]), memoizeWith= fun o n -> log "haha"; false)

// Use somewhere in the code say at the root component
test({|Dispatch=dispatch; test: 5|})

The memoizeWith callback is not called, and for some reason the function keeps getting called infinitely.

If I change it to a non generic then it works fine

let test =
  FunctionComponent.Of((fun (props: {|Dispatch: Msg -> unit; test: int|}) ->
        log props.test
        span [] []
          ]), memoizeWith= fun o n -> log "haha"; false)

// Use somewhere in the code say at the root component
test({|Dispatch=dispatch; test: 5|})

Not sure if this is an issue with Fable or React.

@alfonsogarciacaro
Copy link
Member

Neither, it's an F# thing ;) Generic values are actually compiled as functions and the code is evaluated each time the value is called. If you try the following snippet in F# Interactive, "foo" will be printed two times:

let foo<'T> = printfn "foo"; 5
foo + foo

I acknowledge it's confusing and the need to use generic values if you want to use a generic type in the props makes it unfortunately even more confusing (I also discovered this recently). It's possible to use a generic property if you just use a plain function and instantiate that with ofFunction. But in order to use memoize (which calls React.memo under the hood) you need a value reference, which for generics has the behavior shown above in F#:

let render (props: {| value: 'T |}) = ...

ofFunction render {| value = 5 |} [] // This works

let MyComponent<'T> =
  FunctionComponent.Of((fun (props: {| value: 'T |}) -> ...), memoizeWith=equalsButFunctions)

MyComponent {| value = 5 |} // This creates a new component every time, not the desired behavior

To overcome this, the only solution for now is to create an intermediate function that makes a concrete React component out of the generic function, like this:

let render<'T> (props: {| value: 'T |}) = str ""

let makeComponent<'T>() =
    FunctionComponent.Of(render<'T>, memoizeWith=equalsButFunctions)

let MyIntComponent = makeComponent<int>()

MyIntComponent {| value = 5 |} // The component is memoized as expected

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants