Top React Performance Optimization Tips in 2023

By Ronak Patel   |   9 September, 2022
Top React Performance Optimization Tips in 2023

Quick Summary:

Performance issues in web apps are nothing new, and developers have dealt with these difficulties for quite some time. This is why developers are certain to run into performance concerns whenever a new language is created. One such programming language is React. It has a quick DOM, and it’s fast to the point where it sometimes renders the tree with a lot of useless components. This causes a UI issue, and developers may be unwilling to continue using the language as a result. However, there are various methods for React Performance Optimization.

Before releasing a React app, Dedicated React Developer team should at the very least examine its performance and look for ways to improve the app’s end-user experience. Internally, React employs several innovative approaches to reduce time-consuming DOM transactions required to refresh the user interface. For many applications, utilizing React will result in a fast user experience without requiring extensive performance optimization.

When creating a react application, a lot of thinking goes into how the app should function and look. At the very least, any team or professional developer should examine the app’s performance and search for ways to improve the end-user experience. This step is frequently overlooked, but this post will discuss react optimization techniques for improved performance.

React is a user interface library written in JavaScript. React comes with several options for reducing the number of time-consuming DOM operations required to refresh the UI. For many applications, utilizing React will result in a fast user experience without requiring extensive performance optimization. However, there are several strategies to improve react performance.

Things to consider before Optimizing your React Performance

Before diving right into how to optimize your React performance, you should first take a step back and understand what is causing your React app performance to lag so much. Moreover, optimizing React app shouldn’t be only limited to times when your app starts malfunctioning; it should be a continuous effort to improve your React app performance. Here are certain things to pay attention to before you start optimizing your React app:

React Pre Optimization Tips

What are your current React Performance Bottlenecks?

First, you must identify possible bottlenecks that hamper your React app performance. Luckily React allows developers to gauge the performance of their apps by leveraging Profiler in the React DevTools. Using Profiler, you can record how long it takes for a component to render, understand why a component renders and how different components interact with each other. Such insights will help you get a clearer picture of your React app’s current state, which will help you devise a performance optimization method later.

Which React Component do I need to Optimize first?

Several aspects of an app account for its overall performance and efficiency. However, you can’t work on all such aspects all at once. You need to prioritize what you optimize first. And as a React performance optimization best practice, you should always start by improving the code of your React app. Most performance issues reside in the form of obsolete or poorly written code or codes that don’t serve any purpose apart from adding to the size of the app, leading it to slow down.

When should React update the Component?

React’s default behavior is to run and render the virtual DOM, compare the current DOM with the previous DOM, check for any differences in every component in their props or state, and then re-render the DOM to make those changes. This is not an issue when your React app is small or lacks many components. However, when your app grows in size, re-rendering your React app after comparing it with the entire Virtual DOM for each action will dramatically slow down its performance.

To workaround, this issue, React provides a way for developers to set which component needs re-rendering. It is known as the shouldComponentUpdate method.

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

If the value for this method returns true, it triggers the render-diff process. Now you can control the render-diff method and set which components you don’t want to re-render. You only need to return a false value from the above function to do so. You can compare the current and next state and props to check if they need re-rendering or not.

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

Techniques for Optimizing React Performance

One of the most important factors in optimizing your React app is React speed optimization. The primary strategies listed below can help you improve the functionality of your React project:

Techniques for Optimizing React

Use React. Fragment To Avoid Adding Extra Nodes to the Dom

With React, there are cases where you will need to render multiple elements or return a group of related items. Let’s look at the example.

function App() {
  return (
    <div>
      <h1>Hello React!</h1>
      <h1>Hello React Again!</h1>
    </div>
  );
}

The above code will give you an error stating ” Adjacent JSX elements must be wrapped in an enclosing tag”.  This means that you need to wrap both elements within the parent div.

This will solve the error, but it comes with the risk. Here an extra node is added to the DOM. In a case, like this where a child component is enclosed within the parent component, it creates an issue.

The better way of solving this issue is to use React Fragment which will not add additional nodes to the DOM


function Columns()
 {
  return (
    <React.Fragment>
      <td>Hello React!</td>
      <td>Hello React Again!</td>
    </React.Fragment>
  );
}

You can also use short syntax <></> for declaring fragment.

function Columns()
 
{
  return (
    <>
      <td>Hello React!</td>
      <td>Hello React Again!</td>
    </>
  );
}

Use React. Suspense and React.Lazy for Lazy Loading Components

Lazy loading is a great technique for react performance improvements. The basic idea of lazy loading is to load the component when it’s needed. React comes bundled with React. Lazy API so that you can render a dynamic import as a regular component.  Here instead of loading your regular component like :

import LazyComponent from './LazyComponent';

You can cut down the risk by using the lazy method to render the component

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

React. Lazy takes a function that must call a dynamic import(). This will return a promise which resolves to a module with default export containing a react component

The lazy component should be rendered inside a suspense component, which allows you to add fallback content as a loading state while waiting for the lazy component to load.


import React, { Suspense } from "react";
const LazyComponent = React.lazy(() => import("./LazyComponent"));
function MyComponent() {
  return (
    <div>
      {" "}
      <Suspense fallback={<div>Loading....</div>}>
        {" "}
        <LazyComponent />{" "}
      </Suspense>{" "}
    </div>
  );
}

Use Production Build

While doing react performance testing, make sure you’re testing with the minified production build if you’re benchmarking or having reacted performance issues.

React comes with many useful warnings by default, and these alerts are useful in the development process. However, they make React bigger and slower, so make sure you use the production version when deploying the project.

If you’re not sure if your build process is set up correctly, you can use React Developer Tools for Chrome to check. The icon will have a dark background if you visit a site that uses React in production mode:

react.js web app

The icon will have a red backdrop if you visit a site that is using React in development mode:

React.js

When working on your app, you should use the development mode, and when releasing it to users, you should use the production model. It’s also worth noting that if you’re utilizing React via a CDN, you should update React from development files to production-ready versions.

<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>

Use React. memo for component Memoization

React. Memo is a great way of react js performance optimization as it helps functional cache components.

Memoization is a technique for speeding up computer programs by caching the results of expensive function calls and returning the cached result when the identical inputs are encountered again.

So how does it works? When a function is rendered using this technique, it will save the result in memory, and the next time the function with the same argument is called, it provides the saved result-saving bandwidth.

In React, functions are functional components, and arguments are props. Let’s understand it better with an example. So, how does it function? When a function is rendered using this technique, the result is preserved in memory, and the saved result is returned the next time the function with the same parameter is invoked, conserving bandwidth.

Arguments are props in React, while functions are functional components. Let’s look at an example to help us grasp it better.

import React from 'react';
const MyComponent = React.memo(props => {/* render only if the props changed */});

React. Memo is a higher-order component, and it’s similar to React. Pure component but for using function components instead of classes.

Also Read: Latest React 18 Features & Changes

Flat list Optimization React Native Using React-Window

This reacts performance tuning technique is used to render a large list of data or a table with enormous data, and it can significantly slow down your app’s performance. Virtualization can help in such scenarios like this with the help of libraries like react-windows. It helps solve this problem by rendering only currently visible items in the list, which allows the rendering of lists of any size.


import React from "react";
import { FixedSizeList as List } from "react-window";
import "./style.css";
const Row = ({ index, style }) => (
  <div className={index % 2 ? "ListItemOdd" : "ListItemEven"} style={style}>
    {" "}
    Row {index}{" "}
  </div>
);
const Example = () => (
  <List
    className="List"
    height={150}
    itemCount={1000}
    itemSize={35}
    width={300}
  >
    {" "}
    {Row}{" "}
  </List>
);

Using Production Mode Flag in Webpack

This reacts performance optimization technique is useful for using webpack 4 as a module bundler for the app. You can consider setting the mode option to production. This tells webpack to use built-in  optimization:

module.exports = { mode: "production" };
You can pass it as a CLI argument.

webpack --mode=production

Doing this will limit optimizations, such as minification or removing development-only code to libraries. It will not expose source code, file paths, and much more.

Multiple Chunk Files

Your application will always start with a few elements. You can quickly add new features and dependencies to your system. It’ll result in a massive production file. Consider utilizing CommonsChunkplugin for webpack to segregate your vendor or third-party library code from your application code, resulting in two different files. Vendor.bundle.js and app.bundle.js are the two files you’ll end up with. The browser caches these files less frequently and retrieves resources in parallel, reducing load time.

Avoid Inline Function Definition in the Render Function

The inline function will always fail the prop diff when React conducts a diff check since functions are objects in JavaScript ( {} ! = {} ). Also, if an array function is used in a JSX property, it will produce a new function instance on each render. The garbage collector may have a lot of work on his hands as a result of this.


default class CommentList extends React.Component {
    state = {
        comments: [],
        selectedCommentId: null
    }

    render(){
        const { comments } = this.state;
        return (
           comments.map((comment)=>{
               return <Comment onClick={(e)=>{
                    this.setState({selectedCommentId:comment.commentId})
               }} comment={comment} key={comment.id}/>
           }) 
        )
    }
}

Instead of defining the inline function for props, you can define the arrow function


default class CommentList extends React.Component {
    state = {
        comments: [],
        selectedCommentId: null
    }

    onCommentClick = (commentId)=>{
        this.setState({selectedCommentId:commentId})
    }

    render(){
        const { comments } = this.state;
        return (
           comments.map((comment)=>{
               return <Comment onClick={this.onCommentClick} 
                comment={comment} key={comment.id}/>
           }) 
        )
    }
}

Dependency Optimization

It’s good to analyze how much code you’re using from dependencies while optimizing react app bundle size. You may, for example, be using Moment.js, which offers multi-language localized files. If you don’t need to support several languages, you can remove unnecessary locales from the final bundle with the moment-locales-webpack-plugin.

Let’s take the case of loadash, where you’re only using 20 of the 100+ methods available. Having all those other approaches isn’t ideal in that case. You may do this by removing unneeded functions with the loadash-webpack-plugin.

From here, you can download the list of dependencies you can optimize.

LookinG for React developers to hire?

Get in touch to develop highly scalable web app project.

Throttling and Debouncing Event Action in JavaScript

The number of times an event handler is invoked in a particular time limit is known as the trigger rate. Mouse clicks, on average, have lower event trigger rates than scrolling and mouseover. Higher event trigger rates can cause your program to crash, but there are workarounds.

Determine which event handler is performing the costly work. An XHR request or DOM manipulation that produces UI modifications, for example, processes a lot of data or a calculation event task. Throttling and debouncing strategies can help in these situations without requiring any changes to the event listener.

Throttling

In essence, throttle involves postponing the execution of a function. When an event is triggered, you’ll add a few milliseconds of delay instead of instantly executing the event handler/function. When implementing infinite scrolling, this can be used. You can, for example, defer the XHR call rather than fetching the next result set as the user scrolls.

Throttling can be done in a variety of ways. You can set a limit based on the number of triggered events or the delay event handler that is being used.

Debouncing

Debouncing is a strategy for preventing the event trigger from firing too frequently. If you’re using Lodash, you can use the debounce function to wrap the function you want.

Let’s use an example to grasp this better.


import debouce from 'lodash.debounce';

class SearchComments extends React.Component {
 constructor(props) {
   super(props);
   this.state = { searchQuery: “” };
 }

 setSearchQuery = debounce(e => {
   this.setState({ searchQuery: e.target.value });

   // Fire API call or Comments manipulation on client end side
 }, 1000);

 render() {
   return (
     <div>
       <h1>Search Comments</h1>
       <input type="text" onChange={this.setSearchQuery} />
     </div>
   );
 }
}

If you’re not using loadash, you can use the minified debounced function to implement in javascript.

Function debounce(a,b,c){var d,e;return function(){function h(){d=null,c||(e=a.apply(f,g))}var f=this,g=arguments;return clearTimeout(d),d=setTimeout(h,b),c&&!d&&(e=a.apply(f,g)),e}}

Also Read: How to Secure ReactJS App?

Avoid Async Initialization in componentWillMount()

Only one time, immediately before the initial render, is componentWillMount() invoked. Because this function is run before render, our component will not access the references or DOM element ().

Here’s a terrible example:


function componentWillMount() {
  axios.get(`api/comments`)
    .then((result) => {
      const comments = result.data
      this.setState({
        comments: comments
      })
    })
}

Make component initialization async in the componentDid Mount lifecycle hook to improve it:


function componentDidMount() {
  axios.get(`api/comments`)
    .then((result) => {
      const comments = result.data
      this.setState({
        comments: comments
      })
    })
}

Because props and state are defined during this lifecycle method, WillMount() is useful for handling component setups and doing synchronous calculations based on props.

Avoid using Index as Key for map

While performing performance optimization in react, you often see an index as a key when rendering a list.


{
    comments.map((comment, index) => {
        <Comment 
            {..comment}
            key={index} />
    })
}

Using the key as the index, on the other hand, may cause your app to display erroneous data because the key is used to identify DOM elements. When you push or delete an item from the list, React considers that the DOM element represents the same component as before if the key is the same.

Although it’s typically preferable to use a unique attribute as a key, you can use the shortid module to generate one if your data lacks one.

import shortid from  "shortid";
{
    comments.map((comment, index) => {
        <Comment 
            {..comment}
            key={shortid.generate()} />
    })
}

However, if you have unique property, such as an ID, it’s better to use that.

{
    comments.map((comment, index) => {
        <Comment 
            {..comment}
            key={comment.id} />
    })
}

In the following cases, it’s okay to use the index as the key:

  • The items and list are both static.
  • There are no IDs on the items in the list, and they will never be reordered or filtered.
  • The list is unchangeable.

Use CSS Animation instead of JS Animation

Animations are inevitable for fluid and pleasurable user experiences. There are many ways to implement web animations. Generally, animations are created in three ways:

  • CSS Transitions
  • CSS animations
  • Javascript

Which one is chosen depends on the type of animation we want to add.

When to use CSS -based animation

CSS based Animation

  • To add “one-shot” transitions like toggling UI elements state
  • For smaller, self-contained states of UI elements

When to use JavaScript-Based animation

JavaScript Based Animation

  • When you want to have advanced effects
  • It would help if you had significant control over the animation
  • Need to trigger animation, like mouseover, etc
  • When using requestAnimationFrame for visual changes

Enable Gzip Compression on Web Server

The web server can reduce the file size by using Gzip compression, which means your website will load faster. Because JavaScript, CSS, and HTML files include a lot of repeating text with many whitespaces, gzip works exceptionally well. Because gzip compresses common strings, it can reduce the size of pages and stylesheets by up to 70%, resulting in faster initial rendering.

If you are using Node/Express as the backend, you can use Gzipping to compress your bundle size with the compression module.


const express = require('express');

const compression = require('compression');

const app = express();

// Pass `compression` as a middleware!

app.use(compression());

Using a CDN

A CDN is a terrific way to swiftly and efficiently provide static information from your website or mobile application to your audience. The “edge server” is the closest user request server, depending on the geolocation. The user is connected to the edge server when they request material from your website, supplied through a CDN, and they are guaranteed the best online experience. These are some of the most effective CDN providers on the market. CloudFront, CloudFare, Akamai, MAXCDN, Google Cloud CDN, and others are examples.

You can also host your static content on CDN using Nelilfy or Sugre. sh. Surge is a free CLI tool that automatically deploys your static project to a high-performance CDN.

Avoid initiating Props in the Initial States

It is better to avoid using props in the initial state unless you’re using them only to initialize a component’s internal state, and you’re sure these props don’t need to be updated. Using props to set any component’s initial state is a general anti-pattern in React. Suppose the initialValue in your ParentComponent changes without the component being refreshed; your new set prop value will not be reflected because the constructor function won’t update the current state of the component. This happens because the useState hook initializes the state only once, and when the component gets rendered, it cannot capture the changes made to the InitialValue prop. Using Props in Initial State also leads to duplication of source truth, making it challenging to figure out where the actual data resides.

Using Props to Initiate States looks like this:

class UserPassword extends Component {
  // Constructor function (or getInitialState)
  constructor(props) {
    super(props);
    this.state = {
      confirmed: false,
    };
  }

  render() {
    return <div>{this.props.inputValue && <AnotherComponent />}</div>;
  }
}

What you should do instead:

class UserPassword extends Component {
  // Constructor function (or getInitialState)
  constructor(props) {
    super(props);
    this.state = {
      confirmed: false,
    };
  }

  render() {
    return <div>{this.props.inputValue && <AnotherComponent />}</div>;
  }
}

Memoization of React Components

Memoization in React is a technique intended to boost computer programs by storing the results of costly function calls so that the cached data can be returned when the same inputs are given. This helps speed up your React app since it returns cached results if the query is repeated instead of running the business logic repeatedly, which can take up a lot of resources and time.

Memoization of UserDetails Component Example

Generally, React’s best practice is to create memoized versions of components whose attributes are less likely to change. Let’s take a basic example to understand this.

Suppose you are creating a basic UserDetails React Component. This component would look like this :

const UserDetails = ({user, onEdit}) => {
    const {title, full_name, profile_img} = user;
    return (
        <div className="user-detail-wrapper">
            <img src={profile_img} />
            <h4>{full_name}</h4>
            <p>{title}</p>
        </div>
    )
}

As you can see here, all the children’s components are props-dependent. Hence these stateless components would re-render if and when props change. Since the detail of individual UserDetail is less likely to change, it is ideal to create a memoized version of this component:

import moize from "moize";

const UserDetails = ({ user, onEdit }) => {
  const { title, full_name, profile_img } = user;

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

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

Handle CPU Extensive Tasks with Web Workers

Using Web Workers in React enables running a script operation in a web application’s background, separate from the main execution thread. By shifting the labor-extensive processes to a separate thread, the main thread can run more efficiently without getting slowed down. Web workers can make use of multi CPUs.

Here is how you can add a web worker in React app:

// webWorker.js
const worker = (self) => {
  function generateBigArray() {
    let arr = [];
    arr.length = 1000000;
    for (let i = 0; i < arr.length; i++) arr[i] = i;
    return arr;
  }
  function sum(arr) {
    return arr.reduce((e, prev) => e + prev, 0);
  }
  function factorial(num) {
    if (num == 1) return 1;
    return num * factorial(num - 1);
  }
  self.addEventListener("message", (evt) => {
    const num = evt.data;
    const arr = generateBigArray();
    postMessage(sum(arr));
  });
};
export default worker;
// App.js
import worker from "./webWorker";
import React, { Component } from "react";
import "./index.css";
class App extends Component {
  constructor() {
    super();
    this.state = {
      result: null,
    };
  }
  calc = () => {
    this.webWorker.postMessage(null);
  };
  componentDidMount() {
    let code = worker.toString();
    code = code.substring(code.indexOf("{") + 1, code.lastIndexOf("}"));
    const bb = new Blob([code], { type: "application/javascript" });
    this.webWorker = new Worker(URL.createObjectURL(bb));
    this.webWorker.addEventListener("message", (evt) => {
      const data = evt.data;
      this.setState({ result: data });
    });
  }
  render() {
    return (
      <div>
        <button onClick={this.calc}> Sum </button>
        <h3> Result: {this.state.result}</h3>
      </div>
    );
  }
}

Running this code, the app will calculate the sum of the array with 2M elements. If we didn’t use Web Workers and ran this command on the main thread, the chances of it hanging till the 2M elements get traversed and their total calculated would increase highly. Since we used web workers instead, the main thread can run efficiently parallel to this. The entire computation happens on web workers, and only the result will be communicated to the main thread that only needs to render the result.

Now that we have seen some of the react performance tips.Let’s take a look at some of the best practices to improve react performance.

Also Check – Pros and Cons of ReactJS Web Application Development

React Performance Best Practices

Let’s look at some of the best practices to keep in mind for react app performance optimization.

React Performance Best Practices

Component Interaction

Using excellent tools like React dev tools, you can solve more than 50% of react performance issues in React by examining how your components interacted in your project. However, before figuring out how the components behave, make sure you understand how React works underneath the hood.

Lazy Loading

To keep ahead of the game of speed, it’s best only to load the resources you’ll need right away. Prioritize the resources that must be loaded first and use techniques like code-splitting to load the balance of the code or resources afterward.

Trim JavaScript Bundles

To reduce any code redundancy, audit and trim JavaScript packages. When you’re duplicating things unnecessarily or accidentally, the possibilities are increased. Ensure that they are analyzed and that the amount of your bundled code is determined.

Server-Side Rendering (SSR)

If you’re worried about SEO other than Google, consider whether SSR is truly necessary. SSR isn’t as complicated as it appears. If not done correctly, it can wreak havoc on your app’s performance, possibly leading to disaster.

RESELECT & Immutable.js

If you’re having trouble with performance in Redux-based React projects, try RESELECT and Immutable.js. Reselect is a basic Redux selector library that may be used to create memorized selectors. Selectors can be defined as functions that retrieve chunks of the Redux state for React components.

Wrapping Up!

There are various react optimization techniques to improve react performance, such as lazy loading components, avoiding unnecessary renders, and much more. To make the most of the react app, you need to leverage its tools and techniques. A react web app performance lies in the simplicity of its components, and overwhelming the render-diff algorithm can make your app perform poorly and frustratingly. The techniques outlined above are all great ways for you to practice react js performance optimization for your application.

have a unique app Idea?

Hire Certified Developers To Build Robust Feature, Rich App And Websites.

Get in Touch!

    Ronak Patel

    Ronak Patel is a CEO and Founder of Aglowid IT Solutions, an ever-emerging Top Web and Mobile Development company with a motto of turning clients into successful businesses. He believes that Client's success is company's success and so that he always makes sure that Aglowid helps their client's business to reach to its true potential with the help of his best team with the standard development process he set up for the company.

    Related Posts