loading...
Cover image for Lessons learned and notes from my first ReasonML/ReasonReact app

Lessons learned and notes from my first ReasonML/ReasonReact app

mitchartemis profile image Mitch Stanley Originally published at fullstackstanley.com ・4 min read

I recently released a side project, RSync Command Generator. This is a simple GUI for generating rsync commands for the command line.

Preview of the Rsync Command Generator

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 of type often caught me out. Raw JS syntax is a bit strange as well.
  • Having to use className instead of class - a similar bane to type_ 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!

Copying to clipboard

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.

Web Development with ReasonML

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.

Discussion

pic
Editor guide
Collapse
citizen428 profile image
Michael Kohl

Nice project! You could try turning it into a Revery app for fun :)

Having to use className instead of class

Same in JS, since class is a keyword in ECMAScript.

Things like +. for adding floats

Modular implicits were meant to address this, but I'm not aware of an actively maintained version.

BTW, glad you enjoyed Spin. @tmattio is on here and I did some work on Spin as well.

Collapse
mitchartemis profile image
Mitch Stanley Author

Thanks!

You could try turning it into a Revery app for fun :)

This thought has crossed my mind! I'll give it a try and see how I get on.

BTW, glad you enjoyed Spin. @tmattio is on here and I did some work on Spin as well.

It really is fantastic, I can't stress how much time it saved me. Thank you both :)