How to extend HTML attribute interfaces when designing reasonml react components?

205 views Asked by At

I'm learning reasonml and quite excited about it. Something I often do in typescript react code is:

type Props = React.HTMLProps<HTMLButtonElement> & { foo: boolean }
const SuperButton: React.FC<Props> = (props) => <button {/* stuff with props */ />

In this regard, I communicate to my users as a component library provider that this button extends normal HTML button attributes.

How can I express and extend normal html component attributes in my components?

I see that reason explicitly doesn't support spreading props: https://github.com/reasonml/reason-react/blob/master/docs/props-spread.md.

I do see that there is a composition strategy: How to compose props across component in reason-react bindings?, but not sure how to marry that up with normal HTML element component stuffs.

Any recommendations? Thanks!

1

There are 1 answers

1
Yawar On

It's possible to do something similar using ReasonReact.cloneElement, as Amirali hinted. The idea is to split up your component's props and the HTML button's props into two separate parameters for your component, render your button, and then clone it while also injecting the extra button props.

This page shows a component which encapsulates this clone-and-injection functionality:

module Spread = {
  [@react.component]
  let make = (~props, ~children) =>
    ReasonReact.cloneElement(children, ~props, [||]);
};

Now, you can use this Spread component for your SuperButton component:

module SuperButton = {
  [@react.component]
  let make = (~foo, ~htmlButtonProps) =>
    <Spread props=htmlButtonProps>
      <button> (foo ? "YES" : "NO")->React.string </button>
    </Spread>;
};

The htmlButtonProps prop will contain the regular HTML button props, while separately foo is your component's specific prop. The component can be used like this:

<SuperButton foo=true htmlButtonProps={"autofocus": true} />

Small housekeeping note: you don't actually need to define the modules with the module keyword. If you want you can put them in separate files called Spread.re and SuperButton.re. Reason files automatically become modules.