From zero to production-ready React app

What is a production-ready react app?

This is the first tutorial as a result of me developing Vudo. This article will go through setting up the most production-ready react app that simply says Hello world. This article will not go into much detail about react itself but more about all the tooling and process to go from an empty folder to production-ready react app. By production-ready react app, I mean the following that you usually don’t find with a react tutorial

  1. Linting with eslint: Linting is essential in catching anything from minor style problem to potential bugs with a misplaced ;
  2. Tests with Jest: Jest was a testing framework created by Facebook and it works well with React so that what we’ll be using here
  3. Type checked with flow
  4. Git pre commit and push hooks: To prevent developers from committing bad code, we use pre-commit and pre-push hooks that will run lint & test script before every commit and every push
  5. Packaged by webpack

We can’t call our app a production-ready react app if it’s not in production. The follow up article will go through setting up continuous integration and deploying to S3

  1. Continuous integration and deploy using CircleCI: All commits are automatically built and deployed to S3 using CircleCI.
  2. Hosted by S3 with Cloudfront CDN: Instead of hosting our static files on an web server, we will host them on S3 which will drastically reduce our hosting cost. To make it even faster for our end users, we’ll also setup a cloudfront CDN that will serve our javascript files from a cache that is closest to our end users.

Acknowledgement

Parts of this guide is based heavily on the excellent tutorial js-stack-from-scratch by Jonathan Verrecchia. Jonathan did a great job of explaining what each dependency is and why we need it. The tutorial set up a single project with both server & front end on it. Because we will eventually have a react-native client to Vudo app, I modified it to make this article just for web front end

Prerequisites

Homebrew, node, npm and yarn are installed in the system.

Homebrew is package manager for Mac. As I’m using a Macbook, homebrew makes it really easy to install packages that aren’t shipped with Mac OS X. To install homebrew, go to https://brew.sh/

While this project doesn’t use node, because the service side will use node and run on lambda, we’ll use latest version of node supported by lambda which is 6.10.3 at the time of writing. I highly recommend using nvm to install and manage versions of node

After homebrew, node and npm are installed, run the following command to install yarn

Code

Code for this tutorial can be found at https://github.com/trungduyvu/vudo-web-client/tree/e40439203ea255efd098e836416a4bc10cca9dc4

Let’s start

In the spirit of starting from zero, we will start from an empty project on github, clone and run npm init script

Create an empty project on github and clone it to your computer

npm init will ask a few questions. I went with the default values for most of them. This will create our package.json file

Babel

To install the first set of dependencies, run

babel-cli: Babel is a transpiler for javascript, think of it as a compiler. WhileJavascript is a interpreted language and doesn’t need compiler by default, React uses a special syntax called JSX that looks like HTML except it isn’t.Furthermore, we want to use a few ES2017 Javascript features such as async/await that are not available in Node 6.10. Babel will turn JSX and ES2017 keywords into Javascript that can be run on browsers for us.

Add .babelrc  file to the root of your project with the following content

ESLint

Linters, according to wikipedia, are  tools that analyze source code to flag programming errors, bugs, stylistic errors, and suspicious constructs. ESLint in the standard in linting for javascript. It comes with a set of standard ruleset and addition rules can be installed as plugin. Because you can turn on and off any rule at will, we want to use a widely accepted ruleset so here we choose one based on Airbnb’s excellent style guide. To install, run

Create a .eslintrc.json  at the root of your project and paste the following content to it

This tells ESLint that we want to extends from Airbnb ruleset and we’d be running this in the browser

Add the following script to your package.json

There is a nifty plugin that warnseslint-plugin-compat that warns if you’re using API that are not supported by the version of browser you’re targetting. To install, run

Add this line as top level property to package.json

and add the following to your .eslintrc.json

This will make sure ESLint warn us if we’re using API that are not supported by browsers that command more than 1% market share. Cool right? You can then invoke ESLint by running  yarn lint

Jest

Jest is testing framework from Facebook where React was created as well. I chose it for the following reasons

  1. It comes with everything you need to write tests from the runner to the assertion API. Using another testing library like Mocha, you’d have to couple it with Chai (or other assertion library). One of the main plight that Javascript developers have to deal with is analysis paralysis from having so many ways to do the same thing. So anything that helps reduce the number of libraries is a plus in my case
  2. Snapshot testing: Jest has snapshot testing which is amazing. Since it’s a lot easier to understand once we start writing a few tests, just take my words for its awesomeness right now and I’ll explain it later

To install, run

yarn add --dev jest babel-jest

It’s time to write some tests. Create a src  folder and add an index.js  file with the following content

Then to add test for it, create index.test.js file

And change the test script in package.json  to yarn lint && jest --coverage

To run test:  yarn test

You should see something like the following output

You’re looking at ESLint at work. It detects that the test code uses undefined test and expect functions. Those functions are part of Jest but ESLint doesn’t know that we’re using Jest. To fix this, add the following line to the env section of .eslintrc.json

After that, the test should pass

Flow

Flow is a static type checker for Javascript, also from Facebook. Flow will analyze your code to find bugs due to inconsistent types. For example, if a function requires a string and was passed a number, Flow would detect that and warn us.

To add flow, run yarn add --dev flow-bin babel-preset-flow babel-eslint eslint-plugin-flowtype

That’s a lot of libraries! flow-bin is the flow binary. Because we need to annotate our code with type which makes it invalid javascript code, babel-preset-flow will let babel understand the annotations. babel-eslint let ESLint use babel parser instead of its own. The last library contains linting rules for flow type

Change your .eslintrc.json to the following

The change allows ESLint to use babel’s parser

For babel to parse flow-enabled code, add flow to the list of presets in your .babelrc. The file should be

Add .flowconfig to the root of your project with the following content

This allows you to add

to disable flow check for the next line

Last but not least, update your test script in package.json to run flow after ESLint

Phew!! That’s a lot of work. Let’s test our setup

edit your index.js to the following

We add // @flow  to the top of files that need to be type checked and we can then specify the type of variables or parameter using : <type>  syntax. For list of available types, see Flow docs

Go to our test file and change our test to pass a string to the function instead of a number. For example

Run  yarn test and you should see

That sure looks like a few more errors that we expected. While the last error is what we expected because we pass a string to a function that expects a number, the first two are definitely not expected. The reason is flow cannot find flow type definition for our libraries (jest in this case). The error is vague and it sent me down a rabbit hole trying to fix ESLint for 15 minutes. Hopefully by reading this, you’d not follow that same path.

Introducing flow-typed (see the d?). flow-typed is a repository for library interface definition for use with flow. To install run

flow-typed can look at your package.json file and install any necessary library interface definition. To do that, run

You should see something like this

But we’re not done, we need to tell flow about flow-typed. Add this to your .flowconfig

Finally, your test command should only find one expected error. Fix the test and continue on your merry (or confusing) way

Before you move on though, if you have reservation about flow, we’re very much on the same boat. What if the library I use doesn’t have interface definition on flow-typed? I’m leaving this section in the guide since it may be useful for people who decided that they want to add flow. But feel free to not use flow if you run into trouble along the way

Husky

Husky makes it easy to manage git pre-commit and pre-push hook. We can use it to run an arbitrary script before any commit or push. If the script fails, the git operation will be aborted. We will use these two hooks to make sure that lint and test pass before letting the commit go through

To install:

Add the following two scripts to package.json

Now commit what you have so far and you should see your tests automatically run. Change your test so it fails or make a ESLint violation and commit again. Verify that the commit will fail

Webpack

If you develops website in the 2000’s, you surely remember the steps to add a Javascript library. We would go on to the library website, download the js file, upload it to our server and link to it from our page. The process is time consuming and it makes the website terribly inefficient. If you have 5 js files, the browser would have to make 5 http requests to fetch the files. The page may be blank or not responsive during that time.

To be production-ready, you need something better. Come the technique to bundle your files! Bundling will combine all your js file together therefore saves the browser many http requests. Webpack is the most popular bundler at this point. It also comes with a development server so we can run our app locally.

To install, run

webpack-dev-server is the developer server that we just talked about earlier. babel-loader is a plugin for webpack that transpiles our code. We need both babel-core & babel-loader if we want to transpile our code for the browser

Add src/index.html

index.html is the main html where we will load our script. Notice how we’re hard coding the path of our script to http://localhost:5000/, this is definitely not production ready but we will get to that soon.

Add src/config.js with the following constants

Change your src/index.js to the following content

Both of these files use ES6 version of Javascript (notice the use of export & import) , we’ll use Webpack to create a ES5 bundle of these files so it can be run on a browser

Create a webpack.config.babel.js with the following content

We’re telling webpack a few things

  • That the starting point of our js bundle is in src/index.js
  • The output bundle will be under dist/js/bundle.js and it can be accessed by going to http://localhost:5000/dist/
  • For all files with .js extension except those in node_modules, we’d run them through babel-loader.
  • We would enable sourcemap for easy debuging

Add the following script for your package.json

Okay!! That’s quite a few things. To test our set up, run yarn start on your terminal. You should see something like this

Webpack dev server output

Open a browser and go to http://localhost:5000/src and you should see our Hello Webpack text.

I agree that this is quite possibly the hardest hello world that someone can do, but this set up is geared toward being ready for production, so stay with me and it’ll eventually be worth it

React

It only takes 10,000 steps to finally get to the react part of production-ready react app.

React is a library for building user interface from Facebook. Its innovative approach to building views as well as its ecosystem makes it the most popular front end technology. Mastering it and you can almost guarantee a job. As of this writing, there are 5x more job postings on Indeed for react compared to angular. Let’s start, shall we?

To install react, run yarn add react react-dom

Central to any react apps is the concept of component. Component is an individual piece of code that handle 1 thing in your UI, for example a button can be 1 component. We’d build our entire app by composing and nesting components together. To create our first component, add src/App/App.jsx  . Note: the file name has its first character capitalized and its extension is jsx instead of js. These are part of airbnb style guide and it makes easier to differentiate files that use JSX and files that are react component

Rename your src/index.js to src/index.jsx and put in the following content

We will need the following dev dependencies

Change your .babelrc file to the following

Since we’re using JSX, we need to tell babel how to transform it using babel-preset-react.

Run yarn start and go to http://localhost:5000 and you should see Hello React text

Snapshot testing

If you run yarn test now, it should fail. How can we say this is a production-ready react app if our test fails. Let’s write some unit tests for our new component. I promise you’d learn a great testing technique as a reward for sticking it out all the way to the end.

Let’s install react-test-renderer which allow rendering a react component into an object without the need for the DOM

Then add App/App.test.jsx

Remove index.test.js

Run the test and you should see that it passes. Additionally, there is a new folder __snapshots__  created with App.test.jsx.snap with the following content

When you run a snapshot test, Jest will create a snapshot file that describe how the DOM object will look like, in this case it’s a h1 tag with Hello React! inside

Let’s change your component so it says Hello React new  instead. This time the test should fail because the new message no longer matches the current snapshot.

 

Failed snapshot testing output

Snapshot is a great way to make sure your UI doesn’t change unexpectedly. It can also be used to test API response. Make sure you verify and check in your snapshot files

Deploying to production

Eghh, just kidding. I don’t know about you but I’m exhausted writing this blog. We’ll save deploying to production until the next post. In a meantime, check out the following resource to up your react game

https://reactjs.org/tutorial/tutorial.html
https://learn.tylermcginnis.com/ One of the best react course that I’ve come across. Well worth the money

 

 

1 reply

Trackbacks & Pingbacks

  1. […] part 1, we have set up a simple react app with all the components that you typically find in a high […]

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.