Create a React application from scratch (Part 7): Setting up React and Best Practices

Mostafa Fouad
13 min readJan 24, 2018

--

This post is part of a beginner-level series of posts meant for people who use
ready-made tools, templates or boilerplates for React but wish to learn and understand how to build a React application from the very beginning.

All posts in this series:
Part 1: Introduction
Part 2: Initialization and the First File
Part 3: Using ES2015 Syntax
Part 4: Enforcing a Style Guide
Part 5: Setting up an Express Server
Part 6: Using a Module Bundler
Part 7: Setting up React and Best Practices
Part 8: Setting up Redux
Part 9: Setting up React Router
Part 10: TDD and Setting up Jest

Setting up React

In this post, we are going to setup React and create a very simple component then we will go through some of the best practices that you should keep in mind when developing React components. This is where the fun part starts, so let us dig right in!

Install React and React Dom packages as dependencies:

$ npm install --save react react-dom

Then open up index.js and create a very simple React component that represents our application. We already have an element with id app in our index.pug template file, so let us use it to mount the application.

/**
* index.js
*/
import React from 'react';
import { render } from 'react-dom';
const MainApp = () => (
<h1>Hello React!</h1>
);
// render the app
render(<MainApp />, document.getElementById('app'));

This simple code creates a stateless functional component MainApp and mounts it on a DOM element that has an id app. This code would not work immediately and you would get an error if you try to build the bundle or start the server.

The reason for this error is that we have JSX syntax inside our index.js file which Babel does not yet understand. To allow Babel to interpret this syntax to normal JavaScript, we are going to use the React preset for Babel.

Install the package as a dependency:

$ npm install --save babel-preset-react

Then add the preset to the list of presets in .babelrc file:

{
"presets": [
"es2015",
"stage-0",
"react"

],
"plugins": ["transform-inline-environment-variables"]
}

There should also be a linting error that would prevent you from building the bundle. The linter is complaining because index.js is a JavaScript file that contains JSX syntax but uses the js extension instead of jsx.

You can read the description for this rule here. The ‘When Not to use it’ section says that you should not use this rule if you do not care about restricting the extension of the files that contain JSX syntax.

You can keep using this rule but I prefer to use the js extension for all files so I am going to disable this rule:

{
"extends": "airbnb",
"env": {
"es6": true,
"browser": true,
"node": true
},
"rules": {
"react/jsx-filename-extension": 0
}

}

Enabling HMR

Enabling Hot Module Replacement is as simple as adding a block of code:

/**
* index.js
*/
import React from 'react';
import { render } from 'react-dom';
if (module.hot) {
module.hot.accept();
}
const MainApp = () => (
<h1>Hello React!</h1>
);
// render the app
render(<MainApp />, document.getElementById('app'));

Tips and Best Practices

Before we continue with the tutorial, we will go through an aggregated list of tips and best practices that I have learned from my experience working with React and also from reading and searching around the web. Keep those in mind when you are creating your React components.

Dependency Imports vs Local Imports

Separate the dependency imports from the local imports by a new line. Dependency imports should come first.

import React, { Component } from 'react';
import usefulModule from 'useful-module';
import myLocalModule from './my-local-module';

Stateless Functional Components

If the component is a render-only component or does not need to use a State Object, use a plain JavaScript function instead of a class. This is called a Stateless Functional Component.

So instead of doing this:

import React, { Component } from 'react';class MyComponent extends Component {
render() {
return (
<div>Hello!</div>
);
}
}
export default MyComponent;

Do this:

import React from 'react';const MyComponent = () => <div>Hello!</div>;export default MyComponent;

See how much clutter was removed? You could also make it simpler by exporting the function itself:

import React from 'react';export default () => <div>Hello!</div>;

However, I do not prefer doing this because it makes debugging harder. If you check the React Dev Tools, you will see that the component name is ‘Unknown’ because the function is anonymous.

Anonymous Functional Component

A better approach would be to use a normal named function instead of an anonymous function:

import React from 'react';export default function MyComponent() {
return <div>Hello!</div>;
}
Named Functional Component

Start with Presentational Components

Presentational components are simpler to define, easier to understand and can be re-used over and over because they are independent from the rest of the application.

If you break your application down to a set of presentational components, you can put them all on a single page and tweak their design and variations to accomplish a unified look and feel throughout the whole application.

Build your component as a presentational component and only add state when you need to, which brings us to the next tip.

Minimize Usage of State

Use state sparingly in your components and make sure they use state for UI rather than data, in other words, if you are not using it in render() then it should not be in the state. Remember that you should only use setState if you want to re-render your component.

Let us say that we have a component that consists of a single button. This button may be clicked only once, and when it is clicked, a message will be logged to the console:

import React, { Component } from 'react';

class MyComponent extends Component {
state = {
clickedOnce: false,
};
handleClick = () => {
if (!this.state.clickedOnce) {
console.log('Clicked');
}
this.setState({
clickedOnce: true,
});
}
componentDidUpdate() {
console.log('Updated!');
}
render() {
return (
<div>
<button onClick={this.handleClick}>Click me</button>
</div>
);
}
}
export default MyComponent;

This is an example of a bad implementation, using the state to set a flag clickedOnce which specifies whether the button can be clicked again. Each time the button is clicked, the component will re-render even though it does not need to.

The application re-renders on button click

This would be better:

import React, { Component } from 'react';

class MyComponent extends Component {
clickedOnce = false;

handleClick = () => {
if (!this.clickedOnce) {
console.log('Clicked');
}
this.clickedOnce = true;
}
componentDidUpdate() {
console.log('Updated!');
}
render() {
return (
<div>
<button onClick={this.handleClick}>Click me</button>
</div>
);
}
}
export default MyComponent;

This implementation uses a class property instead of a state key because the clickedOnce flag does not represent a UI state and hence should not live inside the component state. With this new implementation, clicking the button more than once no longer triggers an update.

Always Define propTypes and defaultProps

All components should have propTypes and defaultProps defined as high as possible within the component. They serve as component documentation and should be immediately visible to other developers reading the file.

Since React v15.5, the React.PropTypes has moved into a different package, so let us install that package as a dependency:

$ npm install --save prop-types

For Stateless Functional Components:

Functions are hoisted in JavaScript, which means that you can use a function before its declaration:

import React from 'react';
import PropTypes from 'prop-types';
MyComponent.propTypes = {
title: PropTypes.string,
};
MyComponent.defaultProps = {
title: 'A simple counter',
};
export default function MyComponent(props) {
return <h1>{props.title}</h1>;
}

ESLint would complain about using the function before its definition but for the sake of better component documentation, let us disable this linting rule for functions by modifying the .eslintrc file:

{
...
"rules": {
"react/jsx-filename-extension": 0,
"no-use-before-define": [
"error",
{
"functions": false
}
]

}
}

For Class-based Components:

Unlike functions, classes in JavaScript are not hoisted, so we cannot simply do MyComponent.propTypes = ... before defining the class itself but we can define propTypes and defaultProps as static class properties:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
class MyComponent extends Component {
static propTypes = {
title: PropTypes.string,
};
static defaultProps = {
title: 'A simple counter',
};
render() {
return <h1>{this.props.title}</h1>;
}
}
export default MyComponent;

Initializing the State

State can be initialized within the component constructor:

class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};

}
}

A better way is to initialize the state as a class property:

class MyComponent extends Component {
constructor(props) {
super(props);
}
state = {
count: 0,
};

}

This looks much better, cleaner, more readable and also contributes to component documentation. The state object should be initialized after propTypes and defaultProps:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
class MyComponent extends Component {
// propTypes comes first
static propTypes = {
title: PropTypes.string,
};
// defaultProps comes second
static defaultProps = {
title: 'A simple counter',
};
// constructor comes here
constructor() {
...
}
// then comes the state
state = {
count: 0,
};
}

Pass a function to setState

React documentation discourages relying on this.state and this.props values for calculating the next state because React updates them asynchronously. That means the state may not change immediately after calling setState().

class MyComponent extends Component {
state = {
count: 10,
}
onClick = () => {
console.log(this.state.count); // 10

// count won't change immediately
this.setState({ count: this.state.count + this.props.step });

console.log(this.state.count); // still 10
}
}

While this works for simple scenarios and the state will still be updated correctly, it may lead to unexpected behaviour in more complex scenarios.

Consider this scenario, you have a component that renders a single button. Upon clicking this button, a handleClick method is called:

class MyComponent extends Component {
static defaultProps = {
step: 5,
}
static propTypes = {
step: PropTypes.number,
}

state = {
count: 10,
}

handleClick = () => {
this.doSomething();
this.doSomethingElse();
}
doSomething = () => {
this.setState({ count: this.state.count + this.props.step });
}
doSomethingElse = () => {
this.setState({ count: this.state.count - 1 });
}
render() {
return (
<div>
<h1>Current count is: {this.state.count}</h1>
<button onClick={this.handleClick}>Click me</button>
</div>
);
}
}

The button calls handleClick() when clicked, which in turn calls doSomething() then doSomethingElse(). Both functions will modify the count value inside the state.

Logically, 10 + 5 is 15 then subtract 1 and the result should be 14, right? Well, in this case it is not — the value of count after the first click is 9, not 14. This happens because the value of this.state.count is still 10 when doSomethingElse() is called, not 15.

To fix this, you can use a second form of setState() that accepts a function rather than an object. That function will receive the previous state as the first argument, and the props at the time the update is applied as the second argument:

this.setState((prevState, props) => ({
count: prevState.count + props.step
}))

We can use this form to fix our example:

class MyComponent extends Component {
...
handleClick = () => {
this.doSomething();
this.doSomethingElse();
}
doSomething = () => {
this.setState((prevState, props) => ({
count: prevState.count + props.step
}));

}
doSomethingElse = () => {
this.setState(prevState => ({
count: prevState.count - 1
}));

}
...
}

With this implementation, count is updated properly from 10 to 14 to 18 and so on. Simple Maths makes sense again!

Use Arrow Functions as Class Properties

The this keyword has always been confusing to JavaScript developers and its behaviour is not less confusing in React components. Do you know how this keyword changes in a React component? Consider the following example:

import React, { Component } from 'react';class MyComponent extends Component {
state = {
count: 0,
};
onClick() {
console.log(this.state);
}
render() {
return (
<div>
<h1>Count is: {this.state.count}</h1>
<button onClick={this.onClick}>Click me</button>
</div>
);
}
}
export default MyComponent;

Clicking the button would result in an error:

Uncaught TypeError: Cannot read property ‘state’ of undefined

This is because onClick, as a class method, is not bound by default. There are a few ways to correct this. (pun intended, get it?)

One way is to bind the function to the correct context as you pass it inside the render() function:

<button onClick={this.onClick.bind(this)}>Click me</button>

Or you can avoid changing the context by using an arrow function in render():

<button onClick={e => this.onClick(e)}>Click me</button>

However, these two methods has a slight performance cost because the function would be re-allocated on each render. To avoid this slight performance cost, you can bind the function inside the constructor:

import React, { Component } from 'react';class MyComponent extends Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
... render() {
...
<button onClick={this.onClick}>Click me</button>
...
}
}
export default MyComponent;

This technique is better but you could easily get carried away and end up with something that looks like this:

constructor(props) {
// this is bad, really bad
this.onClick = this.onClick.bind(this);
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.increaseCount = this.increaseCount.bind(this);
this.decreaseCount = this.decreaseCount.bind(this);
this.resetCount = this.resetCount.bind(this);
...
}

Since we use Babel and have support for class properties, the better way would be to use arrow functions when defining the class methods:

class MyComponent extends Component {
...
onClick = () => {
// 'this' is preserved
console.log(this.state);
}
render() {
return (
<div>
<h1>{this.state.count}</h1>
<button onClick={this.onClick}>Click me</button>
</div>
);
}
}

Destruct the Props Object

When a component has many props, destruct the props object placing each property on its own line.

For Stateless Functional Components:

export default function MyComponent({
firstName,
lastName,
emailAddress,
description,
onChange,
onSubmit,

}) {
return (
<div>
<h1>{firstName}</h1>
...
</div>
);
}

Default arguments are not an excuse to drop defaultProps. As mentioned earlier, you should always define propTypes and defaultProps.

For Class-based Components:

class MyComponent extends Component {
...
render() {
const {
firstName,
lastName,
emailAddress,
description,
onChange,
onSubmit,
} = this.props;
return (
<div>
<h1>{firstName}</h1>
...
</div>
);
}
}

This is cleaner, makes it easier to re-order properties and makes it easier to add/remove properties to/from the list while generating a readable diff for Git. Consider the following:

Diff-ing is more readable when each property is on a new line

On the right-hand side, you can easily tell which property was added, however, on the left-hand side you only know that something has changed on that line and you have to look really carefully to figure out which part of the line was changed.

Conditional Rendering

When you need to render one of two components or blocks of JSX code based on a condition, use a ternary expression:

isLoggedIn
? <div>Welcome, {username}!</div>
: <button onClick={this.login}>Login</button>

If the code consists of more than one line, use parentheses:

isLoggedIn ? (
<div>
Welcome, {usename}!
</div>
) : (
<button onClick={this.login}>
Login
</button>
)

If you need to render a single component or a block of JSX code based on a condition, use a short-circuit evaluation instead:

isComplete && <div>You are done!</div>

For more than one line, use parentheses:

isComplete && (
<div>
You are done!
</div>
)

The Key Attribute

It is a common pattern to use Array.prototype.map and similar array methods inside render() function and it is easy to forget about the key attribute. Reconciliation is hard enough, do not make it harder. Always remember to place the key where it belongs to. (pun intended again, get it?)

render() {
return (
<ul>{
{
items.map(item => (
<li key={item.id}>
{item.name}
</li>
))
}
</ul>
);
}

When you use map(),filter() or similar array methods, the second parameter for the callback is the index of the item. It is generally a bad idea to use this index as a key. React uses the key attribute to identify which items have changed, have been added or have been removed and the key value should be stable and should uniquely identify each item.

In cases where the array is sorted or an element is added to the beginning of the array, the index will be changed even though the element representing that index may be the same. This results in unnecessary renders and in some cases results in displaying wrong data.

Use UUID or ShortID to generate a unique id for each item when it is first created and use it as the key value.

Normalize the state

Try to normalize the state object and keep it as flat as possible. Nesting data in the state means more complex logic is required to update it. Imagine how ugly it would be to update a deeply nested field — wait, do not imagine, here:

// the state objectstate = {
...
posts: [
...,
{
meta: {
id: 12,
author: '...',
public: false,
...
},
...
},
...
],
...
};
// to update 'public' to 'true'this.setState({
...this.state,
posts: [
...this.state.posts.slice(0, index),
{
...this.state.posts[index],
meta: {
...this.state.posts[index].meta,
public: true,
}
},
...this.state.posts.slice(index + 1),
]
});

Not only is this code ugly, but it could also force unrelated components to re-render even if the data they are displaying has not actually changed. This is because we have updated all ancestors in the state tree with new object references.

Updating the same field would be simpler if we re-structure the state tree:

state = {
posts: [
10,
11,
12,
],
meta: {
10: {
id: 10,
author: 'author-a',
public: false,
},
11: {
id: 11,
author: 'author-b',
public: false,
},
12: {
id: 12,
author: 'author-c',
public: false,
},
},
}
this.setState({
meta: {
...this.state.meta,
[id]: {
...this.state.meta[id],
public: true,
}
}
})

Use classnames

If you have ever been in a situation where you need to use a conditional classname, you might have written something that looks like this:

<div className={`tab ${isActive ? 'is-active' : ''}`}>
...
</div>

This gets really ugly if you have multiple conditional classnames. Instead of using a ternary, use classnames package:

import classnames from 'classnames';const classes = classnames('tab', { 'is-active': isActive });<div className={classes}>
...
</div>

One component per file

This is not a hard rule but I prefer to keep each component in a single file and default export that component. I have no specific reason for this but I just find it more clean and organised — high five if you do that too! ✋🏻

Conclusion

There is not one right way to develop a React component. We have gone through a few of the best practices and patterns that will help you develop reusable and maintainable components and I am very curious to know what kind of patterns and practices you prefer and why.

In the next part of this series, we are going to build a simple To-Do application and apply some of these best practices and patterns.

Was this article useful? Please click the Clap button below, or follow me for more.

Thanks for reading! If you have any feedback, leave a comment below.

Go to Part 7-b: Building a simple ToDo app (Soon)

--

--