How I’ve Built My First React Application II
Part 2: Building a React application
This is the second article of my “How I’ve Built My First React Application” series, showing the steps I’ve taken, in trying to build an isomorphic voting application using React. All the code is available at my GitHub repo: question-it.
- Part 1: Introduction.
- Part 2: Building.
- Part 3: Data Fetching.
- Part 4: Isomorphism.
The first bump you’d be facing, trying to use React for the first time, would probably be making it work. “Wait, what? it’s JavaScript! I would simply write the code in a .js file, load it with a script tag, and that’s it!”. Well as you may or may not know, React is using JSX to render HTML. While you can create React components without using JSX, it would be a real pain to do that. So let’s just stick with JSX.
What is JSX? In short, it is a JavaScript extension that allows embedding XML in your code. Even the most basic component will implement the render method, which will return JSX corresponding to how to element should render:
import React from "react";
class MyComponent extends React.Component {
render() {
return <h1>This is MyComponent!</h1>;
}
}
export default MyComponent;
If the above code seems like it was written in a different language, that is because it’s (partly) true. The above code uses ES6 features. ES6 is one of the advanced generations of the same old JavaScript we know and love. The problem with this code is, that while simple and elegant, it will fail horribly if you’d try to run it on most browsers. Obviously, that’s not what we want. That’s why we will use Babel.
Babel
Babel is a JavaScript transpiler used to compile “advanced” JavaScript to the “old” supported JavaScript. It’s that simple. You can plug in different presets and transformers, for all the features you feel like using. For React you should install the following:
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
babel-core
: the core package of babel.babel-loader
: babel loader to be used with webpack. We will get to that soon.babel-preset-es2015
: includes all the transformers required to enjoy ES6 features.babel-preset-react
: includes all the transformers required to tranpile JSX and other React requirements.
You would also need to configure Babel to use the presets you’ve just installed. You could do that in your package.json file or in a separate .babelrc file:
{
"presets": ["react", "es2015"],
"plugins": [
"transform-async-to-generator" // transformers you would use
]
}
So we got that covered (well, almost), what’s next?
Webpack
Even medium projects can require many dependencies, requiring us to load each one in a separate script tag. This also requires the browser to fetch each one separately, which can be quite slow. Webpack can help with that by bundling all our code and requirements, into a single file. All that is left is to load that file.
That’s not all webpack does. Webpack can also perform various tasks, usually executed using Gulp or Grunt: Minifying & uglifying, arranging assets such as images and CSS, and compiling (using Babel, for example). Webpack also offers a development server that serves your webpack bundles and enables hot reload: webpack will watch for file changes, build, and reload the page automatically. If you’re not sure whether you really need webpack or not, you can read this.
To install webpack for React:
npm install --save-dev webpack webpack-dev-server react-hot-loader // and any other loader or plugin you would use
react-hot-loader
is a special loader for React to enable hot loading support for React (which webpack doesn’t do).
You configure webpack using a webpack.config.js file. Here is the one I used:
{
context: config.root,
entry: ['client/index.jsx'],
resolve: {
modulesDirectories: ['node_modules'],
extensions: ['', '.js', '.jsx'],
},
output: {
path: config.buildLocation,
filename: 'bundle.js',
publicPath: '/bundle/',
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loaders: [
'react-hot',
'babel'
],
},
{
test: /\.css$/,
loaders: [
'style-loader',
`css-loader?${JSON.stringify({
sourceMap: DEV,
localIdentName: DEV ? '[name]_[local]_[hash:base64:3]' : '[hash:base64:4]',
modules: true,
minimize: !DEV,
})}`,
'postcss-loader',
],
},
{
test: /\.less$/,
loaders: [
'style-loader',
`css-loader?${JSON.stringify({
sourceMap: DEV,
localIdentName: DEV ? '[name]_[local]_[hash:base64:3]' : '[hash:base64:4]',
minimize: !DEV,
})}`,
'less-loader',
],
},
{
test: /\.gif$/,
loader: 'url-loader?limit=10000&mimetype=image/gif',
},
{
test: /\.jpg$/,
loader: 'url-loader?limit=10000&mimetype=image/jpg',
},
{
test: /\.png$/,
loader: 'url-loader?limit=10000&mimetype=image/png',
},
{
test: /\.svg/,
loader: 'url-loader?limit=10000&mimetype=image/svg+xml',
},
{
test: /\.(woff|woff2|ttf|eot)/,
loader: 'url-loader?limit=1&name=/[hash].[ext]',
},
],
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(DEV ? 'development' : 'production'),
}),
],
devServer: {
hot: true,
proxy: {
'*': `${config.appURL}:${config.port}`,
},
host: '127.0.0.1',
},
postcss() {
return [
// your post css plugins (if any)
];
},
}
So let’s go over that file:
- context: the base directory of the project, this is from where webpack will resolve the entry path.
- entry: the “main” file. Webpack will start creating the dependency tree, starting for this file.
- resolve: telling webpack to look for the node_modules directory for modules, from the current directory and upwards. Also tells to search for JSX files in addition to regular js files. output: where to output the bundle file, how to name it, and how it could be accessible from the browser (public path).
- module.loaders: the most interesting part in webpack. here we configure the loaders for our module files. You test for name or extension using the given Regex, include or exclude specific files and directories, and specify the loaders to be used for those files. As you can see here, I load .js and .jsx files with babel and react hot loader. I also load style files using the appropriate loaders, and images and other assets using URL loader. I’d suggest you to google each loader, to learn more about them.
- plugins: webpack can also load various plugins. I just create a simple plugin that loads some data into the environment variable.
- devServer: configuring the webpack dev server, to proxy the real application, enable hot loading, and run on localhost.
- postcss: configuring PostCSS plugins. PostCSS is a JavaScript transpiler for CSS files. Google is your best friend.
Webpack has tons of configuration options: some you might find more useful than others. I suggest you’d read the webpack documentation for more info. all that is left is to build the project:
webpack-dev-server --colors --progress --display-error-details -d --port 8080 --host 0.0.0.0 --hot --inline
webpack dev server will load the bundle files into memory, so you won’t see the bundle.js file in the output folder.
For production build:
webpack --progress --colors -p
And that’s it! Until next time (;