Matthias Le Brun@bloodyowl

Generating ReasonReact components with functors

2019/01/23

In JavaScript, React components can either be function or classes. This makes it easy to create functions that return components (factories if you will).

With ReasonReact though, if you want to use them with JSX, components need to be modules containing a make function, which is a much more static construct.

Fortunately, Reason (through OCaml) has a great feature which is called functors. Think of it as a module function: it takes a module as parameter and returns a module.

The syntax

module Make = (Param: ParamType) => {
  /* contents of the module to return */
};

Let's define a ParamType first:

module type ParamType = {
  type t;
  let name: string;
  let render: t => React.reactElement;
};

Now, let's create our functor. The following example is very short and now really useful, its goal is simply to show the syntax. Where functors shine is with more complicated components, where you just want to write a simple configuration to generate a component (e.g. big virtualised, paginated lists, sortables etc.)

module Make = (Param: ParamType) => {
  type state = {
    value: Param.t,
  };
  let component = React.reducerComponent("Demo" ++ Param.name);
  let make = (~value: Param.t, _) => {
    ...component,
    initialState: () => {value: value},
    reducer: ((), _state) => React.NoUpdate,
    render: ({state}) =>
      <div style={ReactDOMRe.Style.make(~fontSize="32px", ())}>
        /* some markup */
         {Param.render(state.value)} </div>,
  };
};

Now, in order to create a component, we just have to create a module with Make:

module IntValue =
  Make({
    type t = int;
    let name = "Int";
    let render = x => x->string_of_int->React.string;
  });

ReactDOMRe.renderToElementWithId(<IntValue value=2 />, "preview");

Test it on the ReasonML playground

Liked this article?
→ Share it on Bluesky
→ Sponsor me on GitHub