/ ReasonML

BuckleScript Binding Tips

This is a collection of simple binding tips to apply in current or future projects, reducing the need for trial/error and deep docs-searching for solving problems related to the examples below.

Keeping the js in React.js

module Container = {
  [@bs.module "./styled/Container"]
  external js : ReasonReact.reactClass = "default";
  let make = children =>
    ReasonReact.wrapJsForReason(
      ~reactClass=js,
      ~props=Js.Obj.empty(),
      children
    );
};

I name the externals js so that if I ever need to pass the React Component's spec into a JS-based library used in Reason, I can access it with <ComponentName>.js.

For example:

open Bindings.ReactRouter;

let component = ReasonReact.statelessComponent("App");

let make = (_children) => {
  ...component,
  render: (_self) =>
    <Router>
      <div>
        <Header />
        <Switch>
          <Route exact=true path="/" component=Home.js />
          <Route component=NoMatch.js />
        </Switch>
        <Footer />
      </div>
    </Router>
};

Props

Here's a post by Khoa Nguyen about binding ReasonReact props "the right way" using BuckleScript Object "Special Creation Function"s.

Make ReasonReact Components JS-friendly

let component = ReasonReact.statelessComponent("App");

let make = _children => {
  ...component,
  render: self => <div />
};

let default = ReasonReact.wrapReasonForJs(~component, _jsProps => make([||]));

Creating a "default" variable with your component wrapped for JS allows you to simply import it by its BuckleScript output file name instead of worrying about named exports. Example: import App from './App.bs'; instead of import { AppWrapped } from './App.bs';

Non-Code Imports

type assetT;
[@bs.module] external twitterSvg : assetT = "../assets/svg/twitter.svg";

I personally wouldn't assume a type of what the require would output (int, string, etc.) on the JS side. I'd just give it a generic type and let Webpack or Metro Bundler handle it however it would normally.

Globals

[@bs.val] external setInterval : (unit => unit, int) => int = "setInterval";
[@bs.val] external clearInterval : int => unit = "clearInterval";

Global JS variables you want to bind to with BuckleScript are bs.vals, not bs.modules.

Function Overloading

module Date = {
  type t;
  [@bs.new] external fromValue : float => t = "Date";
  [@bs.new] external fromString : string => t = "Date";
};

let date1 = Date.fromValue(107849354.);

let date2 = Date.fromString("1995-12-17T03:24:00");

If the JS function you're binding to takes different types of arguments, you can bind multiple times under different names with different types to denote what you will pass to it.

module Date = {
  type t;
  [@bs.new] external make : ([@bs.unwrap] [ | `Value(float) | `String(string)]) => t = "Date";
};

let date1 = Date.make(`Value(107849354.));

let date2 = Date.make(`String("1995-12-17T03:24:00"));

You can also use bs.unwrap to "overload" your function types.