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";
};
@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>
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----|
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";
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";
To get a value we use the bs.get
attribute:
type user;
[@bs.get] external user: hook => user = "user";
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>;
};
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 */
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)