UPDATED: 4/23/2019
Recently the Reason React project released new bindings. It uses a new version of "react-jsx", and utilizes the React Hooks API instead of the old record based API.
This means that if you have experience with hooks, you'll feel pretty much right at home with Reason React, with some small differences.
With the rise of TypeScript on the front end, I think there will be an increased willingness to learn and utilize typed, compile-to-javascript languages. One strong contender for me is Reason.
You may have heard about Reason and thought about learning it, but it's hard to give up the JavaScript tools you already know and love. The good news is that Bucklescript and tools like Parcel make Reason a breeze to work with.
The following will show you how to set up a Reason React project with Parcel, but teaching you Reason, or how Reason React works compared to React in JavaScript are outside of the scope of this article. If you'd like to learn more about those things, let me know in the comments and I'll gladly write more.
Quick Background
What is Reason?
Reason is a special syntax that will look extremely familiar to JavaScript programmers, that uses OCaml under the hood.
Why does Reason use OCaml under the hood?
Facebook has been using OCaml for a long time to write some of their internal tools (Flow is written in OCaml). OCaml is a fast, mature language that has great type inference, strong type safety, and can compile to many different targets including JavaScript, and Native.
What is Bucklescript?
Bucklescript is a compiler that takes OCaml (or Reason), and turns it into highly optimized, and surprisingly readable JavaScript.
What will we be using today?
The set up we are using will use Reason as the language of our code, reason-react as the front-end framework, Bucklescript for compiling our Reason code to JavaScript, and Parcel for bundling everything up.
Expectations
To get the most out this, you'll want to be comfortable with React.
Initialization
The first we are going to do is make a new folder for our project, and change into the directory.
mkdir reason-parcel
cd reason-parcel
Then we are going to initialize using either npm, or yarn, and install all of our dependencies.
To use the new Reason React bindings you'll want to make sure that you are using bs-platform ^5.0.0, and reason-react ^7.0.0.
yarn init -y
yarn add -D parcel-bundler bs-platform bsb-js
yarn add react react-dom reason-react
Scripts
Now open up your package.json file, and we are going to add a start script. Add the following to the bottom of your package.json
{
"scripts": {
"start": "parcel src/index.html"
}
}
Set up index.html
Let's make a directory called src
. We need to make an index.html
file inside of src
to match the start script we added to the package.json. While we're at it, we might as well make the file where our Reason code will live.
mkdir src
touch src/index.html
touch src/Main.re
You can use whatever HTML boilerplate you'd like. I highly recommend emmet, and if you use VSCode it's already available. Or you can copy the following:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
</body>
</html>
Inside the <body>
tag of index.html add:
<div id="app"></div>
<script src="./Main.re"></script>
There are two important things to note:
- The id of the div is "app". This will be used later inside of our Main.re file to render our app.
- The src property on our script element coincides to the name of the file we just created. Parcel is smart enough to see this src, and know that it needs to process the Reason file, and replace the src property with the outputted JavaScript file path.
Set up bsconfig.json
In the root of your project make a file called bsconfig.json
.
touch bsconfig.json
Bucklescript will read from this file for configuration. If you want to read more about all of the available options for the config file, Bucklescript has great documentation.
Copy the following config into bsconfig.json
{
"name": "reason-parcel",
"reason": {
"react-jsx": 3
},
"version": "0.1.0",
"sources": [{
"dir": "src",
"subdirs": true
}],
"package-specs": {
"module": "commonjs",
"in-source": true
},
"suffix": ".bs.js",
"bs-dependencies": ["reason-react"],
"refmt": 3
}
Let's go through some of the interesting properties:
- reason
- reason-react: Is set to 3 to allow the new JSX features.
- sources
- dir: Is set to the
src
folder we made earlier. - subdirs: Is true because this tells Bucklescript to recursively check within our
src
folder for Reason files.
- dir: Is set to the
-
package-specs
- module: commonjs is the default 👍 which is good enough for us.
- in-source: Will make our outputted JavaScript files appear next to their Reason counterparts.
- bs-dependencies: This is an array of the Reason specific libraries you depend on. Right now we are only using
reason-react
, but you could use component libraries, styling libraries, etc. - refmt: should be set to 3 for the ReasonV3 syntax.
Let's Finally Write Some Reason
Now with all of that out of the way, let's write some Reason code, and view it in the browser!
Open up src/Main.re and copy the following exactly. Semi-colons, double-quotes, and all. We'll run through exactly what is happening shortly.
module HelloWorld = {
[@react.component]
let make = () => <h1>{React.string("Hello, World!")}</h1>
};
ReactDOMRe.renderToElementWithId(<HelloWorld />, "app");
Now save the file, and run yarn start
or npm run start
, and open up localhost:1234/
Alright so there's a few things that are probably familiar, and a few things that are probably weird.
The Familiar
Rendering to element with a specific id:
ReactDOMRe.renderToElementWithId(<HelloWorld />, "app");
That should look pretty familiar to you if you've written React before. It's essentially doing the same as:
ReactDOM.render(<HelloWorld/>, document.getElementById("app"));
JSX
For the most part, JSX in Reason is the exact same. There are some small caveats to HTML attributes that name-clash with Reason reserved words like the type
property on input elements becomes type_
.
The Weird
What is with the module
declaration?
module HelloWorld = {
};
The full explanation goes a little deeper than the scope of this article, but in Reason a file is automatically a module.
To create components in Reason React you need to either declare each new component in a new module, or in a new file. By wrapping our component in module HelloWorld ={};
it allows us to use the JSX element <HelloWorld />
.
What is this [@react.component]?
This is the recommended way to make a new Reason React component. It does some stuff for us behind the scenes which makes our lives easier, but if you want to learn how to manually create your own components without it, check out the new page on components in the Reason React documentation.
What is the make
function?
let make = () => <h1>{React.string("Hello from Reason")}</h1>
Similar to how in React class components React looks for a render method, in a Reason React module, Reason React expects a make function. This make function will take all of the props
as an argument, and simply return some JSX.
Is that really what I have to do to make strings?
React.string("Hello, from Reason")
Yes. Yes it is. At least that it is how you have to make strings that are valid Reason React elements. Stay tuned for the next section for some tips to make strings in Reason React less painful.
Let's Refactor a Little
First let's pull out our component and put it in its own file.
touch src/HelloWorld.re
Then we can copy and paste the HelloWorld module, but this time because it's in its own file so we don't need to wrap it in a module
.
Inside of HelloWorld.re
let s = React.string;
[@react.component]
let make = () => <h1>{s("Hello, from Reason")}</h1>;
I also made it easier to call React.string
by assigning it to a short variable, s
. This technique is something you'll see quite frequently in Reason React code.
Now let's go back to our Main.re
file and delete everything besides the last line.
Main.re
ReactDOMRe.renderToElementWithId(<HelloWorld />, "app");
Notice how we didn't have to explicitly import HelloWorld
into Main.re
? This will seem extremely weird at first, but because of how OCaml's module system works, all file-level modules are globally available under the file name.
So in Main.re
we have access to the HelloWorld module. By using it in our JSX Reason React knows to call the module's make
function.
Now you have all of the pieces necessary for starting a Reason React project with Parcel. Feel free to play around with things, and let me know what questions you have regarding Reason, Reason React, or Parcel.
P.S. I wouldn't feel right without quickly mentioning bsb
as an alternative for bootstrapping Reason React projects:
bsb -theme react -init <PROJECT_NAME>
To use the bsb
command you will have to install it globally
yarn global add bsb
Or use npx (if you're using npm 5.2 or newer you already have it installed) to run the command
npx bsb -theme react -init <PROJECT_NAME>
Top comments (0)