Closed Jacques2Marais closed 3 months ago
Firstly, the ReactComponent
attribute isn't necessary for React.forwardRef
; this function inherently creates a component.
The issue lies in how Fable
handles generics—it transforms them into functions, whereas typically, they might be fields. Consider the following examples:
let NonGeneric = React.forwardRef(fun (props: {| x: int |}, ref) -> Html.span "Hello Non Generic!")
let Generic<'t> = React.forwardRef(fun (props: {| x: 't |}, ref) -> Html.span "Hello Generic!")
// Const - OK, component is defined once
export const NonGeneric = React_forwardRef_3790D881((tupledArg) => {
const props = tupledArg[0];
const ref = tupledArg[1];
return createElement("span", {
children: ["Hello Non Generic!"],
});
});
// Function - not OK, component is re-defined with every render
export function Generic() {
return React_forwardRef_3790D881((tupledArg) => {
const props = tupledArg[0];
const ref = tupledArg[1];
return createElement("span", {
children: ["Hello Generic!"],
});
});
}
There are three solutions:
let StringSpecific = Generic<string>
// It's again a const defined once during module loading
export const StringSpecific = Generic();
Rely on useCallback
for optimization. This approach is somewhat intricate: you must create an additional component that yields your generic component. By wrapping this intermediary component with useCallback
, it ensures that it is instantiated only once.
[<ReactComponent>]
let Generic<'t> x = x |> React.useCallback(React.forwardRef(fun (props: {| x: 't |}, ref) ->
Html.span "Hello Non Generic!"))
export function Generic(genericInputProps) {
const x__1 = genericInputProps.x_1;
const x_ = genericInputProps.x_0;
const x = [x_, x__1];
return useReact_useCallback_1CA17B65(React_forwardRef_3790D881((tupledArg) => {
const props = tupledArg[0];
const ref = tupledArg[1];
return createElement("span", {
children: ["Hello Non Generic!"],
});
}))(x);
}
[Prefered] Rename the ref prop to a different identifier. Given that React plans to eliminate the necessity for forwardRef
(details available here), you can adapt by simply using a different name than ref. The React mechanism only searches for the prop name; no additional effort is required.
I hope this clarifies the core of the issue and allows us to close it. 😄
Thank you @lukaszkrzywizna. I have indeed switched to using the ref prop under a different identifier. I will now close the issue.
I have two components,
GenericInputWithForwardRef
andGenericInputWithoutForwardRef
. Both these components have generic type parameters in the F# function definition. The first component also usesReact.forwardRef
in its definition. Here is the setup:I then use these components as following
The problem is, whenever I change the value of the second component, the first component unmounts and mounts again, thus losing its state and value. After some debugging, I also realized that I forgot to add the
[<ReactComponent>]
attribute above the two component definitions. But doing this causes another issue:Looking at the generated code on line 652 of
FormTesting.fs.js
, I see the following:And the error seems to be right after the part
createElement(GenericInputWithForwardRef, {})
(the first create element). The generated code forGenericInputWithForwardRef
is as followingThe issue seems to be perhaps with Feliz' implementation of
React.forwardRef
? Thank you.