Your first ReasonML PR into an existing React Native Codebase

A delightful plate of Reason. Take a bite! by Amy Treasure on Unsplash

ReasonML is becoming a popular way of building React apps. It’s based on the OCaml programming language with syntax very similar to JavaScript but with a compiler that warns you early on, instead of when your app is live!

If you’ve been sold on ReasonML and you’re ready to incorporate your first component into a personal or better yet — company project!, this article is for you!

Read This First

My goal is for you to get your first PR out the door. I’m skipping over a lot of the “why” and jumping right into “this is how”.

Learning something new is never easy. Making time for learning is already a struggle given how much bosses are piling onto our plates these days. I want to make this as stress-free and simple for you as possible.

If you find that it’s not, just let me know. Until then sit back, relax, stretch your copy-and-paste fingers and follow along. Welcome to Reason.

Installing Reason

A huge convenience of Reason is being able to install everything through npm. That makes it super easy to get started. Read more about it here.

yarn add --dev bs-platform && yarn add reason-react bs-react-native

Create a bsconfig.json

bsconfig.json is a file that supports settings for bucklescript. After looking at a bunch of these, I’ve concluded that they’re usually exactly the same.

Altos is the name of my app. You should replace that 😃

Add Scripts

"scripts": {
 "build": "bsb -make-world -clean-world",
 "watch": "bsb -make-world -clean-world -w"
}

Project Structure

Create a re folder next to package.json This is where all of your components will live. In our bsconfig.json file we have sources that already include re . Here are some other great guidelines.

All your components will need to be capitalized:

  • Typography.re
  • Label.re
  • Add the following to your .gitignore:
.merlin
.bsb.lock
lib

.bs.js files

If you want to sneak Reason into your codebase without forcing your teammates to install bs-platform you’re welcome to add the generated *.bs.js files. That being said, you might face merge conflicts down the road.

If your teammates are all cool with Reason, place *.bs.js inside your .gitignore and have them install bs-platform.

Every team is different so I leave this decision up to you. Chances are you can get away with including them and figure it out later.

Press Play

  1. Open up a terminal window and run yarn watch or npm run watch
  2. Start your React Native Project react-native run-ios or expo start

If nothing broke, you have succeeded in getting your project ready for Reason! Pat yourself on the back — you have taken an enormous step forward to your first Reason pull request!

Delicious bite-size chunks of Reason by Rose Elena on Unsplash Delicious bite-size chunks of Reason by Rose Elena on Unsplash

Your First Component

Creating your first component can be really easy or really hard. If you’re going to try and convert something with a lot of external modules, you’re going to have a bad time.

The first component I introduced into our project was a simple Label:

Label component in ReactJS

The label consumes a prop label and sets the background color to gray. The only components I’m using are View, Text, StyleSheet because the bindings are supported by bs-react-native.

Bindings let ReasonML know the type of your code from Javascript so it can save you from making silly mistakes later.

Converting to Reason

At first glance, a lot of things are the same, but a lot are different. ReasonML requires semicolons! The compiler will delightfully let you know.

The Label component converted to ReactReason.

Imports

In ReactJS and Javascript, you’d import something via import React from 'react' or const React = require('react').

Reason will import the libraries you have installed automatically. We added them in bsconfig.json.

When you see open BsReactNative you’re doing something similar to import { Component } from 'react'. You can import React from 'react' and then call React.Component, or import the Component directly and save yourself some text.

So, instead of writing BsReactNative.StyleSheet, BsReactNative.View I can open BsReactNative and write StyleSheet, View instead.

Local Opens

Style.(

A dot with nothing after it in this case is called a “local open”. Its the same as calling import Style but only for this function. This helps us avoid conflicts across the whole file.

See how within the function we’re calling borderRadius ? We could also do something like this instead:

style=(
 Style.style([
 Style.borderRadius(4.)
]);

(thanks to Sean Grove for pointing this out!)

Styling / StyleSheets

Styling is tricky at first but gets easier over time. Let me save you a lot of time and effort by linking you to all the style types supported by bs-react-native. I’d highly recommend keep this open until you get the hang of it!

Why is styling tricky? Because you’re not used to typing things a certain way. Here’s a great example:

borderRadius(4.)

vs

paddingHorizontal(Pt(10.))

In ReactJS, borderRadius and paddingHorizontal are the same type (number). but in ReactReason, they’re different:

  • borderRadius is a float.
  • paddingHorizontal is a pt_pct. pt_pct isn’t a Reason thing. It’s a type within the bs-react-native library that means:

This can either be a float wrapped by point (pt) or a float wrapped by a percentage (pct)

Components

A stateless function component in ReactJs will be represented by a function:

const Label = ({ label }) => <Text>{label}</Text>;

In Reason React:

*/ Label.re */

[@react.component]
let make = (~label) => <Text> {React.string(label)} </Text>

[@react.component]:

You put this above every component. Think of this like a decorator in Javascript. It knows to convert the sugar to React calls.

The arguments:

  • Optional components look like this: ~label=? with an =? at th e end
  • Default arguments should look familiar: label="Network"

stringToElement

If you try to display your label the same way you would in ReactJS, you’ll be greeted with a compiler error. That’s because you have to convert the string to an React element: ReasonReact.stringToElement(label). Why do we do this? Because Reason’s type system restricts you from passing arbitrary data.

You’ll also notice a space between <Text> </Text>. That’s intentional!

There’s a good chance you’re going to display a number in your first component. That would look like this:

<Text> {ReasonReact.stringToElement(string_of_int(10)} </Text>

There’s a couple of different special element types you might need, but I would strongly suggest you focus on a simple component first.

Exporting your component to JS land

In ReactJS, you’d write export default or export const and consume your component the way you would with anything else.

In ReasonReact, there’s an extra step you have to take via wrapReasonForJs .

let default =
  ReasonReact.wrapReasonForJs(~component, jsProps =>
    make(~label=jsProps##label, [||])
  );

Basically the pattern is to take your props (in this case we only have label) and pass them in as: ~label=jsProps##label . Children will look like: [||] .

Use it!

You’ve done such a great job putting together your first component, it’s time for you to use it. Just import it with the bs extension:

import Label from "./re/Label.bs.js"

<Label label="REACT NATIVE" />

Push it up & let me know!

I challenge you to create one small, tiny Reason component and introduce it into your company’s React Native app. If you do, please let me know!!

Otherwise, until you’re ready follow me on Twitter. You’ll see a lot of Reason-related stuff.

I created a base repo of this & expo as a reference.

Thanks to thangngoc89 and sgrove for peer-reviewing this.

  • Avatar for Peter
  • Avatar for Peter
  • Avatar for Peter
  • Avatar for Peter
  • Avatar for Peter