Hello all, first of all I’m new to OCaml world so I may be missing most of the needed vocabulary to properly explain what I’m trying to achieve, so to make things easier I made a sketch.sh of what I’m trying to do: Sketch.sh - Interactive ReasonML/OCaml sketchbook
Copying and pasting here for anyone that may not be able to access sketch.sh:
Higher-Order Component using Functors
I’m trying to create a Functor that acts as a Higher-Order Component in ReScript React. Since OCaml community is bigger, the code will be in OCaml.
What am I trying to achieve?
I don’t have a use-case for HoC, I was nerd sniped by a friend and now it became a interesting problem to sink time and brain-effort into. But I do have some constraints that, in my mind, makes it “useable” in real world. A HoC has to:
- Be a Functor
- When defining a component using it, I should be able to pass as props the HoC props and child props effortlessly. For example:
<SuperButton hocProp=true childProp=true />
So, without further ado, let’s begin
Component module type
We need to be able to identify a component in our Functor, so let’s create a module type for it.
module type Component =
sig
type props
type makeProps
val makeProps: ?key: string -> makeProps
end
This module signature will not work today in ReScript React because the
@react.componentppx only outputs themakePropsandmakefunctions, and not their typings.
I decided to separate the ?key from the rest of the makeProps arguments because all makeProps definitions have a ?key and we want to be able to pass the key down to the child component.
Creating an Example Component
To illustrate our use-case, we’ll create a ExampleComponent that’s as close to what @react.component outputs as possible
module ExampleComponent = struct
type props = {childProp: string}
type makeProps = childProp: string -> unit -> props
let makeProps ?key ~childProp () = {childProp}
end
For reference, here’s the true output: syntax/tests/ppx/react/expected/commentAtTop.res.txt at master · rescript-lang/syntax · GitHub
Creating the Functor
Now this is where I don’t know how to proceed. Let’s create the first version I thought of and then I’ll explain why it doesn’t meet my criteria and what I don’t know
module Make (C: Component) = struct
let makeProps ~hocProp ?key = C.makeProps ?key
end
This interface is the ideal interface (although the result is not ideal), as it allows us to use it as follow
module MakeExampleComponent = Make(ExampleComponent);;
MakeExampleComponent.makeProps ~hocProp:"prop from the Make Functor" ~childProp:"prop from the child Component" ()
See where it fails to meet the criteria? Although I can pass the ~hocProp, I can’t use it to return the child props and the hoc props. The only ways I can think of is to know which Component instance will be passed to the functor, and create a makeProps that receives all arguments from C.makeProps, apply it to C.makeProps getting the return and creating a return value using it and the hoc props. Like the following
module type Component2 =
sig
type props
type makeProps
val makeProps: ?key: string -> childProp: string -> props
end
module Make2 (C: Component2) = struct
type props = {hocProp: string; childProp: C.props}
let makeProps ~hocProp ?key ~childProp =
let childProp = C.makeProps ?key ~childProp in
{
hocProp;
childProp;
}
end
But this defeats the purpose of a Functor, as I’m not being truly generic over any @react.component
Help Wanted
If this also snipes you, try to make the following compile
module Make (C: Component) = struct
type props = {hocProp: string; childProps: C.props}
let makeProps ~hocProp ?key : props = C.makeProps ?key
end
The above was the last part of the sketch. I would appreciate any comment about if it’s possible or not to do what I’m trying to do