Optimising React Performance

▬▬▬▬▬▬▬▬▬▬▬▬▬ ✦ ✦ ✦ ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬

◉ React Optimisations

A modern front-end library like React doesn’t magically make your app faster. It requires the developer to understand how React works and how the components live through the various phases of the component lifecycle.

With React, you can gain a lot of the performance improvements that it has to offer by measuring and optimizing how and when your components render. And, React provides just the tools and functionalities necessary to make this easy.

you will learn how you can measure the performance of your React components and optimize them to build a much more performant React web app.

React computes the differences between the current UI and the new UI by applying a comparison algorithm on the two versions of its virtual DOM.

Changes = Diff(UI1, UI2)

React then proceeds to apply only the UI changes to the real UI on the browser.

When the data associated with a component change, React determines if an actual DOM update is required. This allows React to avoid potentially expensive DOM manipulation operations in the browser, such as creating DOM nodes and accessing existing ones beyond necessity.

This repeated diffing and rendering of components can be one of the primary sources of React performance issues in any React app. Building a React app where the diffing algorithm fails to reconcile effectively, causing the entire app to be rendered repeatedly can result in a frustratingly slow experience.

◉ Measuring First

React has a powerful tool just for this. Using the react-addons-perf library you can get an overview of your app’s overall performance.

The usage is very simple:

Import Perf from 'react-addons-perf'
Perf.start();
// use the app
Perf.stop();
Perf.printWasted();

This will print a table with the amount of time components wasted in rendering.

The library provides other functions that let you print different aspects of the wasted time separately (e.g., using the printInclusive() or the printExclusive() functions), or even print the DOM manipulation operations (using the printOperations() function).

◉ Should React Update The Component?

By default, React will run, render the virtual DOM, and compare the difference for every component in the tree for any change in its props or state. But that is obviously not reasonable.

As your app grows, attempting to re-render and compare the entire virtual DOM at every action will eventually slow down.

React provides a simple way for the developer to indicate if a component needs re-rendering. This is where the shouldComponentUpdate method comes into play.

function shouldComponentUpdate(nextProps, nextState) {
return true;
}

When this function returns true for any component, it allows the render-diff process to be triggered.

This gives the you a simple way of controlling the render-diff process. Whenever you need to prevent a component from being re-rendered at all, simply return false from the function. Inside the function, you can compare the current and next set of props and state to determine whether a re-render is necessary:

function shouldComponentUpdate(nextProps, nextState) {
return nextProps.id !== this.props.id;
}

◉ Using a React.PureComponent

To ease and automate a bit this optimization technique, React provides what is known as “pure” component. A React.PureComponent is exactly like a React.Component that implements a shouldComponentUpdate() function with a shallow prop and state comparison.

As it only performs a shallow comparison, you may find it useful only when:

  • Your props or states contain primitive data.
  • Your props and states have complex data, but you know when to call forceUpdate() to update your component.

◉ Making Data Immutable

The idea behind using immutable data structures is simple. Whenever an object containing complex data changes, instead of making the changes in that object, create a copy of that object with the changes. This makes detecting changes in data as simple as comparing the reference of the two objects.

More React App Optimization Techniques

◉ Using the Production Build

If you look into React’s source code, you will see a lot of if (process.env.NODE_ENV != 'production') checks. These chunks of code that React is running in your development environment isn’t something needed by the end user. For production environments, all of this unnecessary code can be discarded.

If you bootstrapped your project using create-react-app, then you can simply run npm run build to produce the production build without this extra code. If you are using Webpack directly, you can run webpack -p (which is equivalent of webpack --optimize-minimize --define process.env.NODE_ENV="'production'".

Webpack is the leading module bundler for React and Redux apps.

Webpack’s Core Philosophy

Two core philosophies of Webpack are:

  1. Everything is a module — Just like JS files can be “modules”, everything else (CSS, Images, HTML) can also be modules. That is, you can require(“myJSfile.js”) or require(“myCSSfile.css”). This mean we can split any artifact into smaller manageable pieces, reuse them and so on.
  2. Load only “what” you need and “when” you need — Typically module bundlers take all the modules and generate a large single output “bundle.js” file. But in many real-world apps, this “bundle.js” could be 10MB-15MB and could take forever to load! So Webpack has various features to split your code and generate multiple “bundle” files, and also load parts of the app asynchronously so that you just load what you need and when you need it.

◉ Binding Functions Early

// Creates a new `handleUpload` function during each render()
<TopBar onUpload={this.handleUpload.bind(this)} />

This will cause the render() function to create a new function on every render. A much better way of doing the same is:

class App extends React.Component {
constructor(props) {
super(props);
this.handleUpload = this.handleUpload.bind(this);
}
render() {

<TopBar onUpload={this.handleUpload} />

}
}

◉ Using Multiple Chunk Files

For single-page React web apps, we often end up bundling all of our front-end JavaScript code in a single minified file. This works fine for small to moderately sized web apps. But as the app starts to grow, delivering this bundled JavaScript file to the browser in itself can become a time consuming process.

If you are using Webpack to build your React app, you can leverage its code splitting capabilities to separate your built app code into multiple “chunks” and deliver them to the browser on an as-needed basis.

There are two type of splitting: resource splitting and on-demand code splitting.

With resource splitting, you split resource content into multiple files. For example, using CommonsChunkPlugin, you can extract common code (such as all external libraries) into a “chunk” file of its own. Using ExtractTextWebpackPlugin, you can extract all CSS code into a separate CSS file.

This kind of splitting will help in two ways. It helps the browser to cache those less frequently changing resources. It will also help the browser to take advantage of parallel downloading to potentially reduce the load time.

A more notable feature of Webpack is on-demand code splitting. You can use it to split code into a chunk that can be loaded on-demand. This can keep the initial download small, reducing the time it takes to load the app. The browser can then download other chunks of code on-demand when needed by the application.

You can learn more about Webpack code splitting here.

◉ Enabling Gzip on Your Web Server

React app’s bundle JS files are commonly very big, so to make the web page load faster, we can enable Gzip on the web server (Apache, Nginx, etc.)

Modern browsers all support and automatically negotiate Gzip compression for HTTP requests. Enabling Gzip compression can reduce the size of the transferred response by up to 90%, which can significantly reduce the amount of time to download the resource, reduce data usage for the client, and improve the time to first render of your pages.

Check the documentation for your web server on how to enable compression:

◉ Using Eslint-plugin-react

You should use ESLint for almost any JavaScript project. React is no different.

With eslint-plugin-react, you will be forcing yourself to adapt to a lot of rules in React programming that can benefit your code on the long run and avoid many common problems and issues that occur due to poorly written code.

Code splitting in React.js

We bundle the files in React application using tool such as webpack. Bundling in the end merges the files in the sequence of their imports and creates a single file.

The problem with this approach is that the bundle file gets larger with the increase in files. User may not be use all the feature components but still bundle is loading them, this could affect the loading of application.

To avoid this, code splitting is used in React.

Example

Example of bundling −

// app.js
import { total } from './math.js';
console.log(total(10, 20)); // 42
// math.js
export function total(a, b) {
return a + b;
}

Bundle

function total(a, b) {
return a + b;
}
console.log(total(10, 20)); // 30

The code splitting feature uses lazy loading to load only those files which are required . this can improve the performance of app considerably.

Use of dynamic import is simple use case of lazy loading −

Before

import { total } from './math';
console.log(total(10, 20));

After

import("./math").then(math => {
console.log(math.total(10, 20));
});

The dynamic import is still not part of language standard officially. With babel, we have to use babel-plugin-syntax-dynamic-import.

React.lazy helps to import a component lazily.

import TestComponent from './ TestComponent ';
function LazyComponentLoadExample() {
return (
<div>
< TestComponent />
</div>
);
}

After

const TestComponent = React.lazy(() => import('./TestComponent'));
function LazyComponentLoadExample() {
return (
<div>
<TestComponent />
</div>
);
}

Use of suspense

It’s the fallback content till the lazy loading of the component completes

const TestComponent = React.lazy(() =>import('./TestComponent'));
function SuspenseComponentExample() {
return (
<div>
<Suspense fallback = {<div>Loading...</div>}>
<TestComponent />
</Suspense>
</div>
);
}

◉ Route based code splitting

import { 
BrowserRouter as Router,
Route,
Switch }
from 'react-router-dom';
import React, { Suspense, lazy } from 'react';const Customer = lazy(() = > import('./routes/Customer'));
const Admin = lazy(() = > import('./routes/Admin'));
const App = () = > (
<Router>
<Suspense fallback = {<div>Loading...</div>}>
<Switch>
<Route exact path = "/" component = {Customer}/>
<Route path = "/admin" component = {Admin}/>
</Switch>
</Suspense>
</Router>
);

As React.lazy only support the default export, the named exports are handled with an intermediate export which export them as default.

Experience with Front-end Technologies and MERN / MEAN Stack. Working on all Major UI Frameworks like React, Angular.