Lessons learned and notes from my first ReasonML/ReasonReact app

Mitch

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

// 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

My initial reaction to some of the syntax was an 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.

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.

Spread the word

Share this article

Like this content?

Check out some of the apps that I've built!

Snipline

Command-line snippet manager for power users

View

Pinshard

Third party Pinboard.in app for iOS.

View

Rsyncinator

GUI for the rsync command

View

Comments