React from Scratch Part 2: React by Example

In this series of articles we’re looking at how to get a React web app running without cloning some preconfigured repo or running some magical scaffolding tool. We’ll learn everything needed to get a React app from scratch to production.

In this article we’ll start from where we left off before, and add React.

If you don’t have any code and don’t want to go through the previous article, you can get the code here:

https://github.com/cilliemalan/react-from-scratch/tree/step-1

React: Crash Course

So what is React? There are several good articles that cover the basics of React and whether or not you should use it. I’ll give a very brief intro and you can go and look at the others at your own leisure. What we’ll do for the rest of this guide is dive right in and start writing code, seeing what it does.

In short, React is a tool for managing the DOM for your client-side web app. It does less than a framework than Angular in that it is only concerned about the “View” part (which refers to manipulating the DOM).

You build a React app by building the Components your app is composed of. React components are JavaScript Classes. A Component class has a few life-cycle methods, and a render method that must return an object representing the (vitual) DOM for the component. Components have internal state and props (which are passed by parent components). React will re-render the component whenever state or props change. React does not have data-binding like a framework like Angular or Polymer. Rather you would handle the events from components directly.

The big thing React does is translate changes to the virtual DOM of a component (returned by the render method) to actual changes to the browser DOM. The react Virtual DOM is constructed using a special XML-like syntax called JSX. This means components may look like this:

class Greeting extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}

Obviously this will not fly in a browser so your files need to be transpiled into normal JavaScript to be run on the browser. We’re going to configure Webpack to use Babel for this.

Setting up the build

So let’s install all the deps and get transpilation integrated into our webpack process.

$ npm install --save-dev react react-dom
+ react@15.6.1
+ react-dom@15.6.1
$ npm install --save-dev babel-loader babel-core
+ babel-loader@7.1.2
+ babel-core@6.26.0
$ npm install --save-dev babel-preset-react babel-preset-env
+ babel-preset-react@6.24.1
+ babel-preset-env@1.6.0

Now let’s add babel as a rule for our webpack config:

module: {
rules: [
{
test: /\.(png|svg|jpg|gif)$/,
use: ['file-loader']
},
{
test: /\.(scss|sass)$/,
use: ['style-loader', 'css-loader', 'sass-loader']
},
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['env', 'react']
}
}
}

]
},

As you can see the test will match .js and .jsx files, but not anything in node_modules, and these will use babel-loader configured to use the presets env and react. env makes sure all polyfills and plugins needed are included, and react makes it possible to use JSX syntax.

Now to check if things work, let’s tweak our client/index.js file:

import reactlogo from './content/react.svg';
import './styles/main.scss';
import ReactDOM from 'react-dom';
import React from 'react';
function component() {
return <div>
Hello World
<img src={reactlogo} />
</div>;

}
ReactDOM.render(component(), document.body);

The file is quite a bit shorter and simpler! Let’s run:

$ node server
...
Example app listening on port 3000!
...

And check it out:

It looks exactly as we would expect

To make sure it’s not a fluke, let’s check what it did:

F12 dev console output

As we can see, the element inside body has an attribute called data-reactroot and there are a bunch of comments that easily give away that we are using React!

Another thing you may wonder about is that we didn’t change the file extension to .jsx. This is not necessary because the webpack rule we made will match both .js and .jsx. It will allow JSX syntax in both. We typically will use .jsx extension by convention for React components.

You may be wondering what this magical JSX syntax get’s compiled to…so let’s check. If we open the bundle.js and search for component() we can see the result of the transpilation:

function component() {
return _react4.default.createElement(
'div',
null,
'Hello World',
_react4.default.createElement(
'img', {
src: _react2.default
})
);
}
_reactDom2.default.render(component(), document.body);

We can se here that our JSX has been transpiled by something.createElement. This is really all that the JSX syntax does. Nothing too crazy. It’s even possible to use React without JSX, though I think using JSX is easier.

The Warning

You may also have noticed a warning:

Ominous

The text reads: Warning: render(): Rendering components directly into document.body is discouraged, since its children are often manipulated by third-party scripts and browser extensions. This may lead to subtle reconciliation issues. Try rendering into a container element created for your app.

This is caused by the last line in our index.js:

ReactDOM.render(component(), document.body);

As you can see it’s referencing the body element directly. As the warning message states this can cause problems if a third party script (or even a browser extension) modifies the document.

There are two solutions to this problem. The first would be to programmatically create an element, append it to the body, and use that as our root element. The other option is to modify the HTML template that webpack uses to generate the index.html file, and add an element for our app.

The first option could be done like this:

var app = document.createElement("div");
document.body.appendChild(app);

ReactDOM.render(component(), app);

But since we’re likely to need to change the index template anyway, this is how you do it:

New index.html template

There are several options of how to change the template, but we’re going to use the simplest one by just adding an index.ejs file:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>React from scratch</title>
</head>
<body>
<div id="react-app"></div>
</body>
</html>

Note: webpack will automatically put the bundle script before the </body> tag. We don’t need to do template shenanigans to get it to work.

And then we update our webpack.config.js to use the template (we remove the title completely since we hardcoded it. You can leave it in if you want).

plugins: [
...
new HtmlWebpackPlugin({
template: 'client/index.ejs'
}),
...
],

Now we can update our index.js to use the new root element we created:

const approot = document.getElementById('react-app');
ReactDOM.render(component(), approot);

Now the warning is gone, we’re using our own template, everyone is happy!

Next, let’s look at React components.

React Components

The little bit of “React” we’ve gotten to work yet is basically only using JSX syntax. and patching it into the DOM. To build a proper application we need to divide our user interface into components and build those components using React.

So to start, let’s change our component function into an actual React component.

There are two types of components in React: stateful components, and stateless components. Stateful components are JavaScript classes and derive from React.Component. Stateless components, on the other hand, are JavaScript functions that take their props as arguments.

Our “component” has no state or props, so we can just write it as a stateless component:

function Greeter(props) {
return <div>
Hello World
<img src={reactlogo} />
</div>;
}
const approot = document.getElementById('react-app');
ReactDOM.render(<Greeter />, approot);

Note: React component names must start with a Capital letter.

We just changed the method name and changed the function call to component() to some JSX referencing our greeter function.

Props and Stateless Components

Our component is pretty boring. To show how props work, let’s make it a bit more configurable:

function Greeter(props) {
return <div>
{props.message}
<img src={props.src} />
</div>;
}
const approot = document.getElementById('react-app');
ReactDOM.render(
<Greeter src={reactlogo} message={"Hello World"} />,
approot);

Here we have taken the message and image and promoted them to props that will be passed to the Greeter function.

Stateful Components

Our component does not have any state — it has no internal properties that it can modify itself: all properties related to the function of the component are passed from outside. But for the sake of example, let’s change the component to have some state that it can modify.

Let’s change the component so that clicking on the image will make the text change.

First, let’s change our component to be a React component deriving from React.Component.

import reactlogo from './content/react.svg';
import './styles/main.scss';
import ReactDOM from 'react-dom';
import React from 'react';
class Greeter extends React.Component {
render() {
return <div>
{this.props.message}
<img src={this.props.src} />
</div>;
}
}
const approot = document.getElementById('react-app');
ReactDOM.render(
<Greeter src={reactlogo} message={"Hello World"} />,
approot);

Not much has changed. The render function is still the same apart from taking props as a field and not an argument. props now get’s assigned when the component is constructed and not each time the render function is called.

Note: don’t ever modify props. That’s React’s job.

So let’s add some stateful behavior. Let’s change our component so that it will add an exclamation mark every time the image is clicked.

The first thing we’ll add is a constructor:

class Greeter extends React.Component {
    constructor(props) {
super(props);
        this.state = {
enthusiasm: 0
};
}
...

The constructor calls the super with the passed in props, and initializes the state for the component. Let’s do something with the state:

enthusiasm() {
return "!".repeat(this.state.enthusiasm);
}
render() {
return <div>
{this.props.message}{this.enthusiasm()}
<img src={this.props.src} />
</div>;
}

The enthusiasm method will simply return exclamation marks based on the state. The only thing left is to manipulate the state somehow:

enthuse() {
this.setState(
prevState => ({ enthusiasm: prevState.enthusiasm + 1 }));
}

The method here, enthuse, will update the state and add enthusiasm.

setState

The setState method called here needs some explanation.

The naive way to update state one would imagine would be something like this:

state.enthusiasm++

The problem is that React can’t (easily or quickly) pick up that we did this. So we need something like setState. So why don’t we just do this?

this.setState({ enthusiasm: this.state.enthusiasm + 1 })

The truth is we could. However, to me it’s misleading. Firstly, this does a shallow merge and not a state update. This means it would only replace the properties specified and not all of them. Though this would be exactly what you want in some cases, there is another problem: setState may be asynchronous. The setState method does not actually update the state, but enqueues a state update. To alleviate this the setState method can be called another way:

this.setState((prevState, props) => ...)

the function passed to setState is given the previous state and the props, and should return a new state. This way we avoid depending on non-local, mutable state and instead write state changes using (what can be) pure functions. Much better! So we write our state change like this:

this.setState(
prevState => ({ enthusiasm: prevState.enthusiasm + 1 }));

Notes: 1) we need to put () around the {} so it doesn’t get picked up as a function body, and 2) we don’t need to specify props as an argument if we don’t need it.

Imagine that I’m clicking

Important Note: Binding event handlers

One important thing to note here is that React event handling methods need to be bound. This is due to the confusing way that function invocation works in JavaScript. The problem is regarding the this keyword.

Normally this refers to the the object on which a function was called. For example our render function uses this to get state. I won’t go into detail why (more detail here), but suffice it to say that it doesn’t work that way for event handlers. BUT there are ways we can force it to work this way. One way is to use arrow functions, like this:

<img src={this.props.src} onClick={() => this.enthuse()} />

Arrow functions always consider this to be the same as it was when they were defined. However, this syntax is a bit obscure (for example, here we are throwing away the event argument).

The other way is to bind the functions to this in the constructor.

constructor(props) {
super(props);
    this.state = {
enthusiasm: 0
};
    this.enthuse = this.enthuse.bind(this);
}

This will make it so that inside the enthuse function, this is always what it was told to be when we called bind.

Confusing, I know, but it is what it is.

Multiple Files

Up till now we have had exactly one client javascript file. In the real world we would put components in their own files. Since we are in the real world, let’s do that.

We’ll load our component from a separate file called client/components/Greeter.jsx:

import React from 'react';
export class Greeter extends React.Component {
    constructor(props) {
super(props);
// stuff...
}
    // methods...
    render() {
// code...
}
}

Note: Component filenames, by convention, end with .jsx and start with a capital letter.

Another Note: Though you can export multiple components from a file, we typically put only one per file

And then we import this file from our index.js file:

import reactlogo from './content/react.svg';
import './styles/main.scss';
import ReactDOM from 'react-dom';
import React from 'react';
import {Greeter} from './components/Greeter.jsx';
const approot = document.getElementById('react-app');
ReactDOM.render(
<Greeter src={reactlogo} message={"Hello World"} />,
approot);

Other things

Getting rid of that .jsx suffix

You’ll note that our component is the only one with a .jsx suffix. We can make it that it automatically looks for a .jsx file even if we import it like this:

import {Greeter} from './components/Greeter';

For this we need to tweak our webpack.config.js file:

entry: {
app: ['./client/index.js', 'webpack-hot-middleware/client']
},
resolve: { extensions: ['.js', '.jsx'] },
devtool: 'inline-source-map',

Here we tell webpack to automatically check .js and .jsx files if no extension is specified.

App Component

In the real world we’re going to have more than just a “greeter” component in our application root. For various reasons (e.g. hot reloading) it’s useful to have your whole application wrapped in a single component.

We’ll create our App.jsx file:

import React from 'react';
import reactlogo from './content/react.svg';
import { Greeter } from './components/Greeter';
export function App() {
return <Greeter src={reactlogo} message={"Hello World"} />;
}

And change our Index.js file:

import './styles/main.scss';
import ReactDOM from 'react-dom';
import React from 'react';
import { App } from './App'
const approot = document.getElementById('react-app');
ReactDOM.render(<App />, approot);

Source Maps

At this point, if you’ve been following along, you may have found that javascript debugging was getting a bit slow. If this is the case, you can solve this by changing the devtool property in the webpack config to this:

devtool: 'cheap-module-eval-source-map'

It has some drawbacks, such as only being to specify breakpoints on lines and not statements, but will probably give better performance. As mentioned before, there are other options and I suggest you try them out and pick one that works for you.

debugging using cheap-module-eval-source-map

Chrome Extension

For debugging, there is also a Chrome Extension that helps you see component props and state and other things.

Chrome Extension for debugging React

I imagine there’s a Firefox one as well.

React Hot Module Reloading

If you’ve followed the webpack hot module reload article and want the same features for React, you need to use react-hot-loader:

npm install --save-dev react-hot-loader@next

We’re going to have to add it as an entrypoint in our webpack config:

entry: {
app: [
'react-hot-loader/patch',
'./client/index.js',
'webpack-hot-middleware/client'
]
},

And a plugin for Babel:

use: {
loader: 'babel-loader',
options: {
presets: ['env', 'react'],
plugins: ['react-hot-loader/babel']
}
}

Then we need to update our client/index.js file to catch the hot reload and replace the application. First, we need to wrap our application:

import './styles/main.scss';
import ReactDOM from 'react-dom';
import React from 'react';
import { AppContainer } from 'react-hot-loader';
import { App } from './App'
const approot = document.getElementById('react-app');
ReactDOM.render(
<AppContainer>
<App />
</AppContainer>
,
approot);

Note: The hot reloading will be automatically disabled when doing a production build.

Then we need to catch the hot reloads and re-render the app (add this at the bottom of the client/index.js file):

if (module.hot) {
console.log('hot reloading active');
module.hot.accept('./App', () => {
console.log('doing hot reload');
const NextApp = require('./App').App;
ReactDOM.render(
<AppContainer>
<NextApp />
</AppContainer>,
approot
);
});
}

First it checks if hot reloading is available. If it is it subscribes to hot replace the App component. Notice that on a reload it calls ReactDOM.render again but this time with a re-loaded App component.

Super-hot! And it keeps state too!

Conclusion

In this article we looked at adding React to our webpack build. In the next article we’ll look at building a practical application in React with multiple components.

As before, the code from this article can be found here: