Mastodon hachyterm.io

UPDATE:
ReasonML + BuckleScript is now Rescript.
As the ecosystem has changed around those tools, this blog post is not accurate anymore.


Our reusable Form component works, but we don’t have any routing. How can we navigate to our two forms (register and login)?

Enter ReasonReact Router

It’s a breeze to set up the inbuilt ReasonReact Router. My forays into the world of React Router have been far more painful.

/* src/App.re */

[@react.component]
let make = () => {
  let url = ReasonReactRouter.useUrl();
  switch (url.path) {
  | ["login"] => <Form formType="login"/>
  | ["register"] => <Form formType="register"/>
  | [] => <Main />
  | _ => <NotFoundPage />
  }
}

Yes, that’s all (for development).

Add a let binding for the router API, then pattern-match on different paths.

The default route matches to an empty array. And everything we don’t explicitly handle, we’ll match on the underscore and route to a 404 page.

What about our forms?

On /login we route to our Form component and forward the formType props as "login". And for /register we add the "register" formType.

(I’m still trying to figure out how to set up a base url. For example, when you publish to GitHub pages, you have to tell the router the public url.)

Add Some Pages

Let’s quickly add the placeholder pages for our root route and for the 404 pages.

/* src/Main.re */

let str = ReasonReact.string;

[@react.component]
let make = () =>
  <div className="section is-fullheight">
    <div className="container">
      <div className="column is-4 is-offset-4">
        <h1 className="is-size-1"> {"Main Page" |> str} </h1>
        <hr />
        <a href="/login" className="is-size-4"> {"Login" |> str} </a>
        <p className="is-size-5"> {"or" |> str} </p>
        <a href="/register" className="is-size-4"> {"Register" |> str} </a>
      </div>
    </div>
  </div>;

Nothing new here.

/* src/NotFound.re */

let str = ReasonReact.string;

[@react.component]
let make = () =>
  <div className="section is-fullheight">
    <div className="container">
      <div className="column is-4 is-offset-4">
        <h1 className="is-size-1"> {"404" |> str} </h1>
        <h2 className="is-size-2"> {"Not Found" |> str} </h2>
        <hr />
        <a href="/login" className="is-size-4"> {"Login" |> str} </a>
        <p className="is-size-5"> {"or" |> str} </p>
        <a href="/register" className="is-size-4"> {"Register" |> str} </a>
        <hr />
        <a href="/" className="is-size-4">
          {{j|Back to Homepage|j} |> str} // (A)
        </a>
      </div>
    </div>
  </div>;

Again, it’s a bit of hassle to convert each HTML tag content to a ReasonReact string.
We also have to be careful with Unicode (see line A).

Thoughts

Adding browser-based routing is a dead simple. The API is very intuitive. Setting up the router requires no ceremony.
The router should also be extremely fast because it’s compiled to a jump table in C++.

I haven’t figured out how to work with a base URL yet. It looks like you can pass an url to the ReasonRouter.useUrl function.
I would like to set a public url for development and found a discussion about environment variables on ReasonML Chat.
This is something that I will have to look into later.

Further Reading