React is pretty awesome, but getting started can be tough. Do you use flux? redux? Do you use the new ES6 features and compile with Babel? How do I compile everything with Webpack?
Back in December, 2015 was dubbed "The Year of Javascript Fatigue", and rightfully so. You have all of these new technologies and libraries being developed and before people can decide on a best practice, the next hot library has hit. Maybe you found yourself wanting to try out these cool new things, but quickly felt turned off by how hard it was to get started because literally everyone had an opinion on how you should do it.
Now that the dust has settled a little bit, we're going to take a walk through how to set up a React project using redux for our datalayer, babel for transpiling ES6 features, and webpack for bundling it all together.
For those that are out of the loop, React is a Javascript, component-based view library built by Facebook. "How does this compare to Angular?" you ask. React is just the "view" portion of MV-whatever, allowing you to choose how you architect your data.
Many of the popular state-management libraries follow the action-reducer pattern set out by Flux. Flux at a high level dictates that data only flows in one direction (unlike Angular's two-way data binding) and thus state is managed by a centralized data store. Over time, the Flux pattern of data flow has been refined and simplified, so for the purpose of this how-to, we're going to look at Redux which is a bit easier to grasp.
Getting started
There are two ways you can go about all of this:
- Simply use React and all of the libraries standalone without any build/compilation tools
- Set up a build/compilation environment with things like Babel and Webpack.
Option 2 is more complicated, but probably what you'll run into in a production setting, so we're going to walk through how to set things up. This means that we're going to use the new ES2015/ES7 features provided in Babel and we're going to use Webpack to bundle everything together to distribute in a single javascript file.
Installing Webpack and Babel
First let's initialize our project:
mkdir react-intro && cd react-intro
npm init -y
mkdir src
mkdir src/components
mkdir src/store
mkdir -p dist/js
mkdir server
touch src/main.js
touch server/index.js
Should give us a directory structure that looks like this:
.
├── dist
│ └── js
├── package.json
├── server
│ └── index.js
└── src
├── components
├── main.js
└── store
In your project's directory, we want to run the following to install Webpack and the necessary Babel plugins:
npm install --save \
webpack \
babel-loader \
babel-core \
babel-plugin-syntax-jsx \
babel-preset-react \
babel-preset-es2015 \
babel-preset-stage-0
Our package.json
file now looks like this:
{
"name": "react-intro",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"babel-core": "^6.7.2",
"babel-loader": "^6.2.4",
"babel-plugin-syntax-jsx": "^6.5.0",
"babel-preset-es2015": "^6.6.0",
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "^6.5.0",
"webpack": "^1.12.14"
}
}
Setting up our build process
To compile everything we need to create two files:
- A
webpack.config.js
to tell webpack how to compile everything - A
.babelrc
to tell Babel which presets to load and use
./.babelrc
{
"presets": [
"react",
"es2015",
"stage-0"
]
}
./webpack.config.js
'use strict';
let path = require('path');
module.exports = {
entry: path.resolve(__dirname + '/src/main.js'),
output: {
path: path.resolve(__dirname + '/dist/js'),
filename: 'main.js',
devtoolLineToLine: true
},
module: {
loaders: [
{
test: /src\/.+.jsx?$/,
exclude: /node_modules/,
loader: 'babel'
}
]
}
}
A lot of the webpack config looks scary and complicated, so lets take a few of these sections and break them down:
{
entry: path.resolve(__dirname + '/src/main.js'),
}
This tells webpack that ./components/main.js
is the entry point to our application and where it should start to compile things.
{
output: {
path: path.resolve(__dirname + '/dist/js'),
filename: 'main.js',
devtoolLineToLine: true
}
}
Now we tell it to place the compiled source into a file called main.js
in the ./dist
directory.
{
loaders: [
{
test: /src\/.+.jsx?$/,
exclude: /node_modules/,
loader: 'babel'
}
]
}
This loader is specific to babel. It says that every file in the ./components
directory with a .js
or .jsx
extension can be included and compiled using the presets specified. The presets are babel-specific and give us the functionality of not only ES2015/ES6 features, but also some ES7 features (that's what stage-0
gives us).
Install react, react-redux, react-router, and friends
Now we want to install react, react-redux, react-router, immutable, redux-actions, redux-logger:
- react: will allow us to define and structure our views using JSX
- react-redux: react-specific bindings for redux to manage the state of our app
- react-router: gives us the ability to load specific components for a given route
- immutable: to make detecting changes easier, we're going to use Immutable, this way a simple variable reference comparison will tell us if two objects are equal.
- redux-actions: eliminates a lot of boilerplate when creating redux actions
- redux-logger: a logger middleware that makes it easy to visualize what actions are firing and what data is changing.
npm install --save \
react \
react-dom \
react-redux \
react-router \
immutable \
redux-actions \
redux-logger
Creating a test server
One of the nice things about react-router is that it supports the HTML history API. In order to properly illustrate this and support a hard reload when you navigate to a new page, we're going to run a small Node.js server with express to serve up our client side app and handle server side routing.
Install the dependencies
We'll need two modules - express and serve-static.
npm install --save \
express \
serve-static
The server
Our serve is super simple; just an HTML template that includes our client side app and provides a mount point (<div id="app-root">
) for our application.
'use strict';
const express = require('express');
const serveStatic = require('serve-static');
const path = require('path');
const template = `
<html>
<head></head><body><div id="app-root"></div><script type="text/javascript" src="/js/main.js"></script></body>
</html>
`;
const app = express();
app.use(serveStatic(path.resolve(__dirname + '/../dist')));
app.get('*', (req, res) => {
res.set('text/html');
res.send(template);
});
app.listen(8080, () => {
console.log('server listening on port 8080');
});
Starting the server is as simple as running:
node server
Writing and compiling our first component
As a super basic example to make sure that we have everything set up correctly, we're going to create a single react component and render it to the DOM. Your src/main.js
file should look like this:
'use strict';
import React, { Component } from 'react';
import { render } from 'react-dom';
class TestComponent extends Component {
constructor(props){
super(props);
}
render(){
return (
<h1>Hello World!</h1>
);
}
}
render(<TestComponent />, document.getElementById('app-root'));
To compile, run webpack:
./node_modules/.bin/webpack --config ./webpack.config.js
Start up your server and you should see a nice big "Hello World" on the page.
Ready to start building a more functional application? Stay tuned for part 2!