Lessons learned and notes from my first ReasonML/ReasonReact app
Mitch Stanley
Posted on March 11, 2020
I recently released a side project, RSync Command Generator. This is a simple GUI for generating rsync
commands for the command line.
I’ve been casually learning ReasonML for a while and thought this would be a great project to try it out on. My main goal for ReasonML is to create native apps with ReveryUI, however, I thought I’d give ReasonReact a try first as there’s more resources out there. So here’s my initial thoughts, impressions, and tips to new comers.
Not knowing React is a disadvantage
I’ve used React once about 4 years ago.
I mostly stick with VueJS, EmberJS, or StimulusJS. These 3 frameworks have covered most of my use-cases. I would love it if I could use any of these frameworks with ReasonML. Unfortunately, I’m not confident enough in my ReasonML knowledge to make my own bindings so ReasonReact or Bucklescript-TEA are my options.
The ReasonReact docs are fairly good to get started but they do require base knowledge of React. I had to go and learn about hooks and reducers from various React tutorials. I’m still not sure how to use useEffect
but hey, I know it exists.
Even without this understanding of useEffect
, this has not stopped me from creating an app in ReasonReact. There are plenty of resources out there that can be used to get going.
One of which is the react-hooks starter template for ReasonReact. This has some great examples which I referred to many times.
If you’re building an SPA, I’d recommend checking these for a reference but using spin
(see below) for the actual project structure.
What I Like about ReasonML
- Inferred types.
- Changing state with actions.
- Immutability.
- Being able to fall back to JS when needed.
- Fast compilation and decent compiler feedback.
- The syntax (With some minor exceptions).
// A small glimpse into the structure
type state = {
preferShortFlags: bool,
};
type action =
| TogglePreferShortFlags;
let initialState = {
preferShortFlags: false
};
let reducer = (state, action) => {
switch (action) {
| TogglePreferShortFlags => {...state, preferShortFlags: !state.preferShortFlags}
};
};
[@react.component]
let make = () => {
let (state, dispatch) = React.useReducer(reducer, initialState);
<Container>
<Header/>
/* ... */
</Container>;
};
What I Dont' Like
- Some parts of the language are annoying. Having to use
type_
instead oftype
often caught me out. Raw JS syntax is a bit strange as well. - Having to use
className
instead ofclass
- a similar bane totype_
although I think this is a JS issue rather than because of a Reason keyword. - The documentation is good but I need more of it!
My initial reaction to some of the syntax was a subconscious "nope". Things like +.
for adding floats. These quirks of the language have started to grow on me, though. I think learning more about OCaml, what ReasonML transpiles to, has helped me appreciate these things more.
Reaching for Javascript When You Need To
I only reached for native JS once and that was to copy to clipboard. There is at least one BuckleScript library for this but I could not get it working. The JS ended up looking like this (Not my finest code).
let copy = (dispatch, showEvent, hideEvent) => {
let copyJs = [%bs.raw
{|
function(text, showEvent, hideEvent) {
if (window.clipboardData && window.clipboardData.setData) {
dispatch(showEvent);
window.setTimeout(function() { dispatch(hideEvent)}, 1500);
return clipboardData.setData("Text", text);
}
else if (document.queryCommandSupported && document.queryCommandSupported("copy")) {
var textarea = document.createElement("textarea");
textarea.textContent = text;
textarea.style.position = "fixed";
document.body.appendChild(textarea);
textarea.select();
try {
dispatch(showEvent);
window.setTimeout(function() {dispatch(hideEvent)}, 1500);
return document.execCommand("copy");
} catch (ex) {
console.warn("Copy to clipboard failed.", ex);
return false;
} finally {
document.body.removeChild(textarea);
}
}
}
|}
];
copyJs(command, showEvent, hideEvent);
};
It can then be called like this
<button role="button" ariaLabel="Copy to Clipboard" onClick={_event => copy(dispatch, DisplayNotice, HideNotice)} className="ml-4">
/* ... */
</button>
With this code I passed in actions so that I could toggle showing feedback to the user. It's a bit hacky, but hey, it works!
Deployment with Zeit Now
I deployed the project with Zeit Now. One thing I noticed was that non-root URLs don’t work out of the box. It’s pretty simple to configure them to work with a now.json
file.
{
"routes": [
{ "src": "/", "dest": "/index.html" },
{ "src": "/common", "dest": "/index.html" }
]
}
For dynamic URLs check the documentation.
Useful links
The Reason React Documentation Examples are great for understanding how to communicate between components and using state.
Rock Your Code (@hisophiabrandt) has a great series on ReasonReact which I very much enjoyed reading through. Includes some great external resources, too.
spin - Project scaffolding tool. I wish I knew about this from the start. It has an excellent starter template for ReasonReact which includes Router setup and an option for using TailwindCSS.
Reason React Hacker News - Great project for referencing how to do things.
Real World OCaml - Great for giving context to how ReasonML works.
Would I use it again?
Absolutely. Overall, I really like ReasonML, and I hope its popularity grows.
I think for now I will continue to use this for smaller projects while I get to grips with it. Hopefully I’ll get confident enough to release a native ReveryUI app in the near future.
Posted on March 11, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.