Converting JS to ReasonML with Bindings — Part 3
The rest of the Converting JS to ReasonML with Bindings Series can be found here: Part 1, Part 2, Part 3, Part 4.
Where Do We Start
If we take a look at the imports on layout.js
we see a few things we need to handle.
So we need to a StaticQuery
, Header
, Helmet
and to import some css.
Header
Let’s start with creating the reason version of Header.re
.
Create Header.re
in the components
directory.
These are functions we need to convert.
We already got the Link
from our our reason-gatsby-link
import so we dont have to work about that.
The last three we will create using bucklescript like we did before.
The `navigate` biniding
For navigate
from @reach/router
we don't even have to look anything up. We know there is an export called navigate
on the @reach/router
module. So let try talking through the binding out loud.
bucklescript get module
@reach/router
and access it with a function callednavigate
that takes a string. When you get the string, call the function on the@reach/router
navigate
function.unit
is the keyword that calls the function for us.
And this is what it looks like in code:
The `auth.js` binding
The auth.js
file is a module like any other. Instead of importing it, you wrote it yourself. So we call it the exact same way. Talking through it out loud,
bucklescript get the module
auth
at../utils/auth
path and access it with a function calledlogout
that takes any old type'a
. When you get the'a
thingy, call the function on the../utils/auth
module with the namelogout
. I'm giving you the'a
type because this function takes a call back and I actually don't know how to type that correctly just yet so just take any old thing until i figure it out.
The `isLoggedIn` binding
Talking to ourselve out loud again:
For
isLoggedIn
create a function calledisLoggedIn
which expects to get a boolean type back. Everytime I call it, immediately hitisLoggedIn
at../utils/auth
which should return a true or false.
This is the code version of those two statements:
This is the logout
function in auth.js
:
We see that it takes a callback so I know that I have to pass it one. I am not sure how to type it which is why I used the 'a
type annotation which basically means take anything at all.
In Header.js
logout
was called like this:
Reading this, I see that first we are calling signOut
on the Amplify module. Just to mirror the Header.js
file so you could see what is going on, I ripped it from Amplify.re
and redefined it here. Later we will just call it from there.
So, we are calling signOut
then logging the user out by clearing out the local store on the local machine. If that works, call navigate
to take the person back to the app/login
page and log any errors.
To do this, I had to go find some code I read somewhere on git hub showing how to do a js style try/catch statement. I haven’t seen that documented anywhere so that is a great example of why reading other people’s code helps you solve problems. I saw how it was used here and came up with this:
Which I passed to the onClick
function here:
This is a ternary function which first checks if the user is logged in or out. If the user, is logged in, then show the dom element with the signout button on it. If not, don’t show it. That is the ReasonReact.null part. You could have used just <div />.
In the onClick
key, the value is set to a function that listens for the click _event
which we preceed with an underscore to let the compiler know that we won't be using the event, we just want to know that it happened. Then when it happened, call our signOut
function. Note the signature:
We defined it as taking the amplify
type and then calling unit. Here it is again.
[@bs.send] external signOut: (amplify, unit) => unit = "signOut";
It doesn't take any params. Just tells Amplify to sign out anyone who is signed in right now. Then we go to sign the person out on this machine. Our sign in code, which we have not discussed yet, saves a token to local storage. Apparently, when we call logout
fromauth.js
we are deleting the token. We almost don't have to care. We just give the function what it wants and let it go to work. In this case, it wants a call back to check for errors…
Sidenote: I went to go get you the api for Js.Exn and ran into the documentation for the Js style try on the bucklescript docs. That, my friends, is how you build the knowledge. I am not even going to write this post pretending I knew that. Here it is, we have learned it at the same time.
So the callback calls navigate
and lets gives any Js erros using the Js.Exn.Error
module from bucklescript. Thanks, @bobzhang1988.
So know we are calling auth.js
from our reason file which is calling back out to javascript files. Interop Madness people. You are not ready!!!
Here is what the working code looks like:
Go back into src/layout.js
and replace the Header
import. Change:
to:
import Header from './Header.bs'
Run gatsby develop
to refresh if it didn't. Rejoice!
Show me the code. See tag creating-layout-component-header-import.
Incidentally, as most of you know, there are plenty of ways to do this. I am just doing piecemeal so you can see how easy it is without making huge changes. By the end we will have refactored to get the full power of ReasonML.
I was going to try to re-create Helmet.js
in Reason just to systematically go through this with you all but it got a bit convoluted. I went to get some help on the discord which I want to invite you to join, and decide not to do it after talking to some of the folks on there.
Getting and Giving Help
Important point on community help and mental health. I have rules to keep my spirit up when I get frustrated. In relevant part, if you haven't asked someone the question, you are the @$$hole in the room. So go to www.stackoverflow.com or www.reason.chat or Reason's Discord channel and ask the damned question. If it's an important problem, get the answer on stackoverflow or reason.chat because it will be easier to find later. I don't know how many times I have looked something up and come up on my own previous question or even answer to someone and found the answer while doing an internet or github search. Sharing helps everyone.