React By Example: Part 5

John Tucker
Frontend Weekly
Published in
4 min readJun 4, 2017

Re-implementing the counter application in React.

This article is part of a series (starting with React By Example: Part 1) of articles that, through a number of progressively more complicated examples, explores the React JavaScript library. The examples are available as a GitHub repository.

In this article we will re-implement our counter application with React and introduce several new React concepts: state and event handling.

Laying out the Render Method (Re-Implement)

We begin by stubbing out our application with:

create-react-app re-implement

Then we update the App component’s render method:

update to src/App.js

render() {
return (
<div>
<div>0</div>
<button>+</button>
</div>
);
}

and remove the unneeded imports of logo.svg and App.css.

To run, enter the following command from the project folder:

yarn start

Observations

  • The result is the number zero with a button below it; but no functionality, i.e., clicking button does not do anything.
  • We had to add an extra div entry to wrap the two div and button entries; if you think about how JSX translates to JavaScript it become clear why this is needed.

The State

In the reference application implementation, the current value (state) of the counter was stored in the DOM itself (the counter element). Each time we increment the counter we need to read the value of the counter out of the DOM before we write it back to the DOM. The issue here is that DOM operations are comparatively expensive (slow) and at scale will significantly impact the performance of the application.

Alternatively, with React we will keep the state of the counter as a property of the component (initialized in the class constructor); class properties and constructors are JavaScript ES2015 class features.

updates to src/App.js

...
class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0,
};
}
render() {
...

Observations:

  • The counter state is actually stored as a property of the property state of the component; used to consolodate the entire state of the component as a single JavaScript object.
  • Component’s constructors accept a single parameter, props (short for properties); we will cover this feature in a later article.

We than update the render method to use this state.

updates to src/App.js

...
render() {
const { counter } = this.state;
return (
<div>
<div>{counter}</div>
<div>{counter === 0 ? 'zero' : counter.toString()}</div>
<button>+</button>
</div>
);
}
...

Observations

  • As we make changes to the source code, the browser will automatically re-render (due to the yarn start command we used earlier).
  • The extra div entry is provided to illustrate that the contents inside of the curly braces can be any JavaScript; in this case a ternary operator that is often used in JSX.

Event Handling

Thinking about our reference implementation, we need to handle button clicks.

updated src/App.js

...
<button
onClick={() => window.console.log('CLICKED')}
>+</button>
...

Observations

  • Now clicking the button will generate output in the application console (available through Chrome Developer Tools).
  • In the sample React application, the properties of JSX entries translated into CSS classes (className) and HTML attributes, e.g., src. In this case, however, the onClick property adds a click event listener (the provided function) to the button.
  • In vanillaJS, the equivalent operation would have used the addEventListener function.
  • What is not obvious is that onClick property also causes React to remove the event listener when the component is no longer visible, i.e., unmounted. We will explore how a component can be unmounted in a later article.

The React feature, automatically removing irrelevant event listeners, is another important benefit of using React.

Without a library like React, in a more complex application, we would be adding and removing DOM elements as the user interacts with it. One common pain-point is having to use removeEventListener for each of the added event listeners before removing a DOM element (especially problematic in the case of removing an entire branch of the DOM). One common error is neglecting to remove the event listeners; resulting in a memory leak of listeners and detached DOM elements.

Updating State

Instead of writing to the console, we want to increment the counter when we click the button.

updated src/App.js

<button
onClick={() => this.setState({ counter: counter + 1 })}
>+</button>

The inherited (from Component) method setState does two things:

  1. It sets the component’s state property based on the single parameter.
  2. It then triggers the component’s render method; thus updating the DOM.

As a performance enhancement, React does not immediately update the DOM, rather it first updates a copy of the DOM (called the virtual DOM) and only updates differences between them. This is important as rendering the DOM is an expensive operation.

This feature is a critical to the design of React as it allows us to declaratively (as opposed to imperatively) render the DOM with minimal performance impact.

For example, in our reference implementation the click handler imperatively (directly) updates the contents on the counter element. On the other hand, in our React re-implementation, the click handler updates the state and instructs the whole component to re-render itself (declarative). Using the virtual DOM, React actually only updates the contents of the counter element in the DOM.

To illustrate how fragile imperative implementation are, imagine that we had implemented the current re-implementation (with the two counter divs) in vanillaJS. In this case, the click handler would need to imperatively update both divs on a click. Say then we later add some additional functionality that updates the counter value; that functionality would also have to imperatively update both divs. Here we have introduced an opportunity for an error by having to keep multiple implementations synchronized.

The Next Part

In the next part, React by Example: Part 6 , we introduce two new React concepts: stateless functional components and properties.

--

--

John Tucker
Frontend Weekly

Broad infrastructure, development, and soft-skill background