DEV Community

Cover image for ReasonReact, Auth0 and 3rd Party React Components
Paul Bacchus
Paul Bacchus

Posted on

ReasonReact, Auth0 and 3rd Party React Components

One of the ways I like to test a technology is to try and implement something that could be required in a real world app, and authentication is one of those things. I have also been wanting to see how easy it is to use a third-party JS React component with ReasonReact. So, to kill two birds with one stone I created a simple app that uses the Auth0 React component. You can view the app here, and the code is here.

Using the Auth0Provider component

The login for the app is based on the Auth0 quick start tutorial for React, and the first thing we have to do is wrap our App in the Auth0Provider. In JavaScript we would do import { Auth0Provider } from '@auth0/auth0-react'; and wrap our App component with the imported Auth0Provider component, but in ReasonReact we need to write some bindings to the component using Melange attributes.

module Auth0Provider = {
  type auth_param = {redirect_uri: string};

  [@bs.module "@auth0/auth0-react"] [@react.component]
  external make:
    (
      ~domain: string,
      ~clientId: string,
      ~authorizationParams: auth_param,
      ~children: React.element
    ) =>
    React.element =
    "Auth0Provider";
};
Enter fullscreen mode Exit fullscreen mode

@bs.module is a Melange attribute that lets us bind to a value from a JS module. The = "Auth0Provider" at the end is the JavaScript name of the value/component we are binding to. The external before the make is used for JS interop/FFI and is like an FFI let binding. Everything else in between is the type signature of a React component in ReasonReact.

In Reason each file is a module, and it is common to have one ReasonReact component per file with the filename being the name of the component. We can also define modules within files which is what we have done for the Auth0Provider in the snippet above, and we use the component like so:

<Auth0Provider
  domain="dev-dj37pygvjwvaxjde.us.auth0.com"
  clientId="e6mvq0WBov5jSBW0clTFdAIXgbyyK4gv"
  authorizationParams={redirect_uri: origin ++ "/profile"}>
  <App />
</Auth0Provider>
Enter fullscreen mode Exit fullscreen mode

Using the useAuth0() hook

Now that we have wrapped our App with the Auth0Provider we can start using the useAuth0() hook in our components, and to do so we must write some bindings to the hook.

Because in JavaScript we would import the hook we again have to use the @bs.module attribute to bind to it:

type hook;
[@bs.module "@auth0/auth0-react"] external useAuth0: unit => hook = "useAuth0";
|--------------------1-------------------| |---2---| |-3-|  |-4-|   |----5----|
Enter fullscreen mode Exit fullscreen mode

Here we are telling Reason that we are binding to an external something in the module "@auth0/auth0-react"(1) and that something is called "useAuth0"(5) in JS land; we are binding "useAuth0"(5) to the name useAuth0(2) in Reason land; and useAuth(2) takes a unit(3) (i.e., takes no arguments; equivalent to useAuth0() in JS) and returns a hook(4). We have to give the return value a type (type hook) so that the type system knows what we have just "imported"/bound to, and which we need when we want to bind to the values and methods of the hook.

(You don't have to use the same JS name in Reason for a binding. For example, you could do something like:

[@bs.module "@auth0/auth0-react"] external useAuth0Reason: unit => hook = "useAuth0";
Enter fullscreen mode Exit fullscreen mode

You would then just use useAuth0Reason in your ReasonReact code. In fact, you may need to rename bindings if they clash with reserved key words in Reason.)

The useAuth0 hook provides various state values and methods, and to use them we need more bindings.

To call a method we use the bs.send attribute:

[@bs.send] external loginWithRedirect: hook => unit = "loginWithRedirect";
Enter fullscreen mode Exit fullscreen mode

To get a value we use the bs.get attribute:

type user;
[@bs.get] external user: hook => user = "user";
Enter fullscreen mode Exit fullscreen mode

Once defined we can use the bindings in our ReasonReact components:

// LoginButton.re
[@react.component]
let make = () => {
  let ua = Bindings.Auth0.useAuth0();

  <button
    className="py-2 px-4 rounded-md bg-blue-500 text-white"
    onClick={_ => Bindings.Auth0.loginWithRedirect(ua)}>
    {React.string("Log In")}
  </button>;
};
Enter fullscreen mode Exit fullscreen mode

And the very readable output JavaScript of the LoginButton ReasonReact component:

// Generated by Melange

import * as React from "react";
import * as Auth0React from "@auth0/auth0-react";

function LoginButton(Props) {
  var ua = Auth0React.useAuth0();
  return React.createElement(
    "button",
    {
      className: "py-2 px-4 rounded-md bg-blue-500 text-white",
      onClick: function (param) {
        ua.loginWithRedirect();
      },
    },
    "Log In"
  );
}

var make = LoginButton;

export { make };
/* react Not a pure module */
Enter fullscreen mode Exit fullscreen mode

nice

Bindings can be written as they are needed, and once written completing the Auth0 quick start guide is pretty straight forward.

Conclusion

Third-party JS React components can be successfully used in any ReasonReact project. Bindings may look complicated, but once you've written a couple of them you quickly get the hang of it.


Cover image by Miguel Á. Padriñán from Pexels

Top comments (0)