Optimizing Performance in React

Manvi Chaddha
Last Updated: May 13, 2022

Introduction

When building any web application, a lot of thought goes into how the application should work and end up looking; the major concern is the functionality and user experience. One important thing to remember is that for a good user experience, the application should be optimized for best performance. Internally React uses several different techniques to minimize the number of costly DOM operations required to update the UI. However, several other techniques can be used to experience a significant increase in performance, speed, and overall user experience for your applications.

This blog will discuss some of the techniques for optimizing performance in React.

(Source: Giphy)

Technique 1: Use Production Build

When developing a React application, it is in development mode. That means React includes many helpful warnings; these warnings are useful in development time. However, they make your application slower and larger.

This problem can be fixed by using production build when you deploy the application. If your application is built with create-react-app, you can fix this by running npm run build before deploying. This will create a production build for your application in a build folder that you can then deploy.

Checking whether your application is in development mode or production mode can be confirmed using the React Developer Tools Google Chrome Extension as illustrated below:

  1. If the react icon is blue with a dark background, it signifies your app is in production mode.

2. If your React app is in development mode, the icon switches to a red background, as displayed in the image below.

If you use React via CDN, you should remember to update React from development files to production ones.

<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

Technique 2: Using React Fragment

Let's take a look at an example wherein we need to render multiple elements or return a group of related items.

function App() {
  return (
    <h1>Hello World</h1>
    <h1>Hello World Again</h1>
  );
}

Run your app with the code above. You will encounter an error stating that Adjacent JSX elements must be wrapped in an enclosing tag, meaning that you need to wrap both elements within a parent div. Wrapping the elements inside a div container will fix the error, but it comes with a risk as you add an extra node to the DOM. A better way to fix this is by using React.fragment  Fragments let you group a list of children without adding extra nodes to the DOM.

function App() {
  return (
   <React.Fragment>
    <h1>Hello World</h1>
    <h1>Hello World Again</h1>
    </React Fragment>
  );

You can also use the short syntax <></> for declaring a Fragment.

Technique 3: Component Memoization

You may be familiar with the word, Memoization. It's a pattern to solve dynamic programming problems in a top-down approach. In the context of React, Memoization means storing the result of expensive function calls and returning the cached results when the same inputs occur again. Functions are functional components, and arguments are props. Let's take a look at an example of a stateless React component to understand this

const StudentDetails = ({user, onChange}) => {
 const {rollno, full_name, profile_img} = user;

 return (
     <div className="user-detail-wrapper">
         <img src={profile_img} />
         <h4>{full_name}</h4>
         <p>{rollno}</p>
     </div>
 )
 }

All the children are based on props; the stateless component will re-render every time props change. If the component is less likely to change, then it's a good choice to use the memoized version of the component as shown below:

import moize from 'moize';

const StudentDetails = ({user, onChange}) => {
 const {rollno, full_name, profile_img} = user;

 return (
     <div className="user-detail-wrapper">
         <img src={profile_img} />
         <h4>{full_name}</h4>
         <p>{rollno}</p>
     </div>
 )
}

export default moize(StudentDetails,{
 isReact: true
});

Technique 4: Handling Lazy Loading Components

(Source: Giphy)

In React, a great technique for optimizing and speeding up your App is Lazy Loading. The concept of lazy loading is to load a component only when needed. Code splitting your app can help you "lazy-load" just the things that the user currently needs. A real-life example is Pinterest which utilized code-splitting techniques to take down the size of their bundles from 650kb to 150kb. As a result, app loading time also improved from 23 seconds to 6.5 seconds.

(Source: simform.com)

What we do is, instead of loading a component like: import Component from ‘./Component’; you use the lazy method to render the component:

const Component = React.lazy(() => import('./Component'));


The React.lazy takes in a function that calls a dynamic import(), which then returns a Promise that resolves to a module with a default export containing a React component. It is essentially important to render the Lazy component inside a Suspense component. It allows to add fallback content as a loading state while waiting for the lazy component to load. The following example illustrates the same

import React, { Suspense } from 'react';
  
const LazyComponent = React.lazy(() => import('./LazyComponent'));

function UserDetails() {
 return (
 <div>
   <Suspense fallback={<div>Loading....</div>}>
     <LazyComponent />
   </Suspense>
 </div>
);
}

Technique 5: Using shouldComponentUpdate

Before moving to the optimization part, let's quickly recall how components are rendered in React: React maintains an internal state of the rendered UI, this lets React avoid creating DOM nodes and accessing existing ones beyond necessity as it can be slower than operations on JavaScript objects, it is also known as virtual DOM. When a component props or state changes, the newly created element is compared with the previously rendered one; when they are not equal, React will update the DOM.

(Source: Giphy)

Now you might be wondering what is the problem then?

The problem is even though React only updates the changed DOM nodes, re-rendering still takes some time. If the slowdown is noticeable, you can speed this up by overriding the lifecycle function, shouldComponentUpdate, which is triggered before the re-rendering starts. The default implementation of this function returns true, leaving React to perform the update:

shouldComponentUpdate(nextProps, nextState) {
  return true;
}


Suppose you know that in some situations, your component doesn't need to update. In that case, you can return false from shouldComponentUpdate instead to skip the whole tendering process, including calling render() on this component and below.

                                                                                           (Source: Giphy)
Let's look at an example subtree of components and try to understand when React hand to update the DOM and when there is no need to update the DOM.

(Source: Telerik)

  1. For the subtree, rooted at C2, the shouldComponentUpdate returned false, React did not attempt to render C2, and thus didn’t even have to invoke shouldComponentUpdate on C4 and C5.
  2. For C1 and C3, shouldComponentUpdate return true, so React had to go down to the leaves and check them. For C6, shouldComponentUpdate return true, and since the rendered elements weren't equivalent, React had to update the DOM.
  3. For C8,  React had to render this component, but since the React elements it returned were equal to the previously rendered ones, it didn’t have to update the DOM.


Note that React only had to do DOM mutations for C6, which was inevitable. For C8, it bailed out by comparing the rendered React elements, and for C2’s subtree and C7, it didn’t even have to compare the elements as we bailed out on shouldComponentUpdate, and render was not called.

Technique 6: Virtualize Long Lists

Rendering long lists of data can become problematic and greatly reduce the application's performance if not optimized while developing. List virtualization or windowing is a technique to improve performance when rendering long lists of data; this technique renders only a small subset of the rows at any given time and, in turn, can dramatically reduce the time it takes to re-render the components and the number of DOM nodes created. React libraries like react-window and react-virtualized provide several reusable components for displaying lists, grids, and tabular data. The below example illustrates the windowing in React.js

import React from "react";
import ReactDOM from "react-dom";
import { FixedSizeList as List } from "react-window";


import "./styles.css";


const Row = ({ index, style }) => (
  <div className={index % 2 ? "ListItemOdd" : "ListItemEven"} style={style}>
    Row {index}
  </div>
);


const Example = () => (
  <List
    className="List"
    height={400}
    itemCount={1000}
    itemSize={35}
    width={300}
  >
    {Row}
  </List>
);


ReactDOM.render(<Example />, document.getElementById("root"));


The corresponding web application looks like this:

There are several other techniques as well for Optimizing Performance in React. Feel free to explore them while learning to use React in projects.

Frequently Asked Questions

Q1) What makes ReactJS performance faster?
Ans 1) React makes use of a virtual DOM, which minimizes the number of costly DOM operations. In addition, there are several techniques for Optimizing performance in React.

Q2) Which hook is used for optimizing performance?
Ans 2) React allows developers to use a useMemo() hook, where we can use the optimization technique of Memoization.

Key Takeaways

This blog discussed techniques for Optimizing performance in React. ReactJS is one of the most popular frameworks; you may refer to our Guided Path. Are you Planning to ace the interviews of reputed product-based companies like Amazon, Google, Microsoft, and more? Attempt our Online Mock Test Series on CodeStudio now!

Was this article helpful ?
0 upvotes

Comments

No comments yet

Be the first to share what you think