3

I am currently writing a material-UI reason-react binding and I want to know how I can re-use previously define Props.

The Select component spreads all of the Input props into itself, in the underlying react-js lib. this is done by spreading props however this is discouraged in ReasonML as typings are lost.

As a temporary solution, I have copied the props from one to another but this is not scalable. I would appreciate if someone could suggest what is the correct way of doing this in Reason-React?

Thanks

Input module definiton:

module Input = {
  [@bs.module "material-ui/Input"] external reactClass : ReasonReact.reactClass = "default";
  let make =
      (
        ~disableUnderline: option(bool)=?,
        ~disabled: option(bool)=?,
        ~error: option(bool)=?,
        ~autoFocus: option(bool)=?,
        ~fullWidth: option(bool)=?,
        ~style: option(ReactDOMRe.style)=?,
        ~value: option(string)=?,
        ~onChange: option((ReactEventRe.Form.t => unit))=?,
        ~placeholder: option(string)=?,
        ~className: option(string)=?,
        ~inputType: option(string)=?,
        children
      ) =>
    ReasonReact.wrapJsForReason(
      ~reactClass,
      ~props=
        Js.Nullable.(
          {
            "disableUnderline": unwrap_bool(disableUnderline),
            "disabled": unwrap_bool(disabled),
            "error": unwrap_bool(error),
            "fullWidth": unwrap_bool(fullWidth),
            "autoFocus": unwrap_bool(autoFocus),
            "style": from_opt(style),
            "placeholder": from_opt(placeholder),
            "className": from_opt(className),
            "type": from_opt(inputType),
            "value": from_opt(value),
            "onChange": from_opt(onChange)
          }
        ),
      children
    );
};

Select module definiton:

    module Select = {
  [@bs.module "material-ui/Select"] external reactClass :  ReasonReact.reactClass = "default";
  let make =
      (
        ~autoWidth: option(bool)=?,
        ~classes: option(Js.t({..}))=?,
        ~className: option(string)=?,
        ~displayEmpty: option(bool)=?,
        ~input: option(ReasonReact.reactElement)=?,
        ~inputClasses: option(Js.t({..}))=?,
        ~native: option(bool)=?,
        ~multiple: option(bool)=?,
        ~menuProps: option(Js.t({..}))=?,
        ~renderValue: option((unit => unit)),
        ~value: option('a)=?,
        ~style: option(ReactDOMRe.style)=?,
        /* Input Props*/
        ~disableUnderline: option(bool)=?,
        ~disabled: option(bool)=?,
        ~error: option(bool)=?,
        ~autoFocus: option(bool)=?,
        ~fullWidth: option(bool)=?,
        ~value: option(string)=?,
        ~onChange: option((ReactEventRe.Form.t => unit))=?,
        ~placeholder: option(string)=?,
        ~className: option(string)=?,
        ~inputType: option(string)=?,
        children
      ) =>
    ReasonReact.wrapJsForReason(
      ~reactClass,
      ~props=
        Js.Nullable.(
          {            
            "autoWidth": unwrap_bool(autoWidth),
            "classes": from_opt(classes),
            "className": from_opt(className),
            "displayEmpty": unwrap_bool(displayEmpty),
            "input": from_opt(input),
            "InputClasses": from_opt(inputClasses),
            "native": unwrap_bool(native),
            "multiple": unwrap_bool(multiple),
            "MenuProps": from_opt(menuProps),
            "renderValue": from_opt(renderValue),
            "value": from_opt(value),
            "style": from_opt(style),
            /* Input Props*/
            "disableUnderline": unwrap_bool(disableUnderline),
            "disabled": unwrap_bool(disabled),
            "error": unwrap_bool(error),
            "fullWidth": unwrap_bool(fullWidth),
            "autoFocus": unwrap_bool(autoFocus),
            "style": from_opt(style),
            "placeholder": from_opt(placeholder),
            "className": from_opt(className),
            "type": from_opt(inputType),
            "value": from_opt(value),
            "onChange": from_opt(onChange)
          }
        ),
      children
    );
};
user465374
  • 1,521
  • 4
  • 20
  • 39

1 Answers1

3

You can use currying and Js.Obj.assign to achieve this:

let common = (reactClass, props, ~commonProp1, ~commonProp2, children) =>
  ReasonReact.wrapJsForReason(
    ~reactClass,
    ~props=Js.Obj.assign(props, {
      "commonProp1": commonProp1,
      "commonProp2": commonProp2
    }),
    children
  );

module Input = {
  [@bs.module "material-ui/Input"] external reactClass :  ReasonReact.reactClass = "default";
  let make = (~inputProp1, ~inputProp2) => common(reactClass, {
    "inputProp1": inputProp1,
    "inputProp2": inputProp2
  });
};

module Select = {
  [@bs.module "material-ui/Select"] external reactClass :  ReasonReact.reactClass = "default";
  let make = (~selectProp1, ~selectProp2) => common(reactClass, {
    "selectProp1": selectProp1,
    "selectProp2": selectProp2
  });
};

In each make function common is partially applied, and due to currying will "extend" the make function with its own arguments. In effect, the type signature of e.g. Input.make will be ~inputProp1 => ~inputProp2 => ~commonProp1 => ~commonProp2 => ....

glennsl
  • 28,186
  • 12
  • 57
  • 75