Let's say I have a higher-order component, something like the following trivial definition, exported from the JavaScript module ./hoc.js:
export const withStrong =
Component => props =>
<strong> <Component ...props/> </strong>
Assuming I have some component called HelloMessage, what is the equivalent of this piece of JavaScript:
import { withStrong } from './hoc.js';
const HelloMessage = ...
const StrongMessage = withStrong(HelloMessage);
ReactDOM.render(
<StrongMessage name="Joe" />,
document.getElementById('react-app')
);
TL;DR:
This should be the exact equivalent of the requested JavaScript snippet:
There'a also a runnable example on the Reason playground with a few adaptations made to work around not having a separate JavaScript file.
Explanation follows:
Binding
withStrongis just a function. It happens to be a function that accepts and returns a react component, which is a bit mysterious, but they're really just values like any other. We can just bind it like an ordinary function.Even something as simple as this would work
assuming you always make sure to pass in a component. But it wouldn't be particularly safe as you can pass it anything else too, so let's try to use the type system as it should be used, restricting it to only accept react components.
The ReasonReact source code says components have the type
component('props), so that's what we'll use.Using the
'propstype variable in both the argument and return type means we constrain them to be the same. That is, the returned component will have exactly the same props as the one passed in, which is exactly what we want in this case.And that's really all there is to the binding itself. we can now use it like this:
Unfortunately this doesn't support JSX. To render
strongMessageas is we'd have to write something likeNot great. So let's fix that.
JSX
transforms to
So we need a
StrongMessagemodule with two functions,makeandmakePropsthat conform to what's expected byReact.createElementVariadic.makeis just the component itself, so that's simple enough.makePropsis a function that acccepts the props as labeled arguments terminated byunit(since the props may be optional) and returns a js object. This also happens to be exactly what[@bs.obj]does, which isn't in any way coincidental.Putting this together then, we get:
And that's it! Yay!
Addendum: Shortcuts
Ok, so the
makePropsfunction is a bit of an annoying mouthful. Fortunately in our case, where the props of the wrapped component is the same as the original, it's also unnecessary sinceStrongMessage.makePropswill be identical toHelloMessage.makeProps. Let's just steal that then! And now we haveBut we can do even better! By using
include HelloMessagewe can dropmakePropsentirely (thanks to @bloodyowl, via @idkjs, for this one).That's pretty nice, isn't it? This works because
include HelloMessagewill include all the exported definitions fromHelloMessagesuch asmakeProps, but alsomakeand anything else. This is probably what you want when you wrap a component in this way, but beware that it imports and re-exports everything from the included module, in case that's not what you want.Usage
Finally, once we have both the binding and JSX in order we can use it like this