Search code examples
reasonreason-react

Handling children in ReasonReact


I am trying to build my own <MySelect> component where the user passes in their own <option>s. Like this:

<MySelect label="Pick a color" onChange={...}>
    <option value="blue">(ReasonReact.string("Blue"))</option>
    <option value="red">(ReasonReact.string("Red"))</option>
    <option value="green">(ReasonReact.string("Green"))</option>
</MySelect>

But I don't understand how to handle the children within the MySelect. The docs (and here) doesn't show a full example.

Attempt

/* ... */
make = (~label, ~onChange, children) =>
    <label>
        <div>(ReasonReact.string(label))</div>
        <select onChange=onChange>
            ...children
        </select>
    </label>

I get

Error: function call with [@@bs.val "createElement"]  is a primitive with [@bs.splice], it expects its `bs.splice` argument to be a syntactic array in the call site and  all arguments to be supplied

Without the spread (so children instead of ...children) I get

Did you pass an array as a ReasonReact DOM (lower-case) component's children?
If not, disregard this. If so, please use `ReasonReact.createDomElement`:
https://reasonml.github.io/reason-react/docs/en/children.html

Here's the original error message
This has type:
  array('a)
But somewhere wanted:
  ReasonReact.reactElement

I am sure both these error messages combined with the docs tell me all that I need to know; there are just too many unknown words for me to make sense of it.

I have also attempted to google for examples (it would seem like a fairly normal usecase), but everywhere I find, they ignore the children entirely.


Solution

  • select is a DOM (lowercase) component so you'll need to use createDomElement. The relevant section in the docs is here: https://reasonml.github.io/reason-react/docs/en/children.html#pitfall. To modify your example a little bit with an example:

    module MySelect = {
      let component = ReasonReact.statelessComponent("MySelect");
    
      let make = (~label, ~onChange, children) => {
        ...component,
        render: _ =>
          <label>
            <div> (ReasonReact.string(label)) </div>
            (
              ReasonReact.createDomElement(
                "select",
                ~props={"onChange": onChange},
                children,
              )
            )
          </label>,
      };
    };
    
    module Example = {
      let component = ReasonReact.statelessComponent("Example");
    
      let make = _children => {
        ...component,
        render: _ =>
          <MySelect label="test" onChange=(value => Js.log(value))>
            <option value="blue"> (ReasonReact.string("Blue")) </option>
            <option value="red"> (ReasonReact.string("Red")) </option>
            <option value="green"> (ReasonReact.string("Green")) </option>
          </MySelect>,
      };
    };