Introduction

Performance is the main factor in the quality of an application. Performance depends on how we are coding and configuring the infrastructure. Performance optimization is an important technique to consider before delivering any application. Because it will impact the user experience, in this article, we are going to explore performance optimization techniques in React.

Performance optimization in React

React uses the Virtual DOM concept to minimize the number of DOM operations. Other than this, developers should consider some important optimization factors while developing the React web application.

React.Fragment

Most of the time, the developer wants to render multiple or groups of elements, then they will wrap those elements within a parent "div" element.

<div>
    <h1>Title</h1>
    <p>Content</p>
</div>

In this case, you are adding an extra node to the DOM. It is an unnecessary node because it is used just to wrap the group of elements. I think if we keep on adding unnecessary extra nodes in the application, then it will impact the performance. How do you remove this in the best way?

<React.Fragment> allows us to group the elements without adding an extra node.

<React.Fragment>
    <h1>Title</h1>
    <p>Content</p>
</React.Fragment>

There is an alternate syntax for React.Fragment. You can also use the short syntax <></> for declaring a <React.Fragment>.

<>
    <h1>Title</h1>
    <p>Content</p>
</>

Using production built-in Webpack

If "webpack" is a module bundler for your application, then you have to set the "mode" option as "production" in the webpack config file. It means "webpack" to use the built-in optimization during the bundling of the application.

// webpack.production.config.js
module.exports = {
  mode: 'production'
};
//or pass it as a CLI argument
webpack --mode=development

Use lazy loading components

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

Please read the article Lazy Loading in React to learn more about Lazy Loading In React with examples.

Implement shouldComponentUpdate()

import React from "react";
export default class TestComponent extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      data: ""
    }
  }
  handleClick = () => {
    let value = document.getElementById("textvalue").value;
    this.setState({ data: value })
  }
  shouldComponentUpdate(nextProps, nextState) {
    if (nextState.data === this.state.data)
      return false
    return true
  }  
  render() {
    console.log("Rendering");
    return (
      <div>        
        <input id="textvalue" type="text" />
        <button onClick={this.handleClick}>Click</button>
        {this.state.data}
      </div>
    )
  }
}

Consider, if the above code doesn't have the "shouldComponentUpdate()" method then, the component re-renders every time of button clicks (even the text box has the same value).

Rendering

In the above code, the "shouldComponentUpdate()" lifecycle method has been implemented explicitly. In this method, the previous value is compared with the current value and if both are the same returns false (which means no re-rendering) otherwise returns true (re-render the component).

Rendering

React.PureComponent

The above "TestComponent" has been implemented using "React.PureComponent". Here, the component itself handled the "shouldComponentUpdate()" method for both shallow comparison and re-rendering.

import React from "react";

export default class TestComponent extends React.PureComponent {
  constructor(props) {
    super(props)
    this.state = {
      data: ""
    }
  }
  handleClick = () => {
    let value = document.getElementById("textvalue").value;
    this.setState({ data: value })
  }
  render() {
    console.log("Rendering");
    return (
      <div>        
        <input id="textvalue" type="text" />
        <button onClick={this.handleClick}>Click</button>
        {this.state.data}
      </div>
    )
  }
}

Use memoization

const UserDisplay = (userDetails) =>{
    const {name, age, address} = userDetails;

    return (
        <>
            <h4>{name}</h4>
            <p>{age} , {address}</p>
        </>
    )
}
export default React.memo(UserDisplay);
// First - UserDisplay component gets called and executed, and then rendered.
<UserDisplay
  name="Test"
  age="30"
  address="Test address"
/>
// Second - The cached result will render without any execution.
<UserDisplay
  name="Test"
  age="30"
  address="Test address"
/>
// Third - UserDisplay component gets called and executed, and then rendered.(because here value of the name value different)
<UserDisplay
  name="New Test"
  age="30"
  address="Test address"
/>

Use Functional/Stateless Components

The functional component prevents the construction of the class instance. It reduces the overall bundle size better than classes.

Binding the Functions in Early

// Creates a new "onChange" function during each render()
<input type="text" onChange={this.onChange.bind(this)} />
// ...as do inlined arrow functions
<input type="text" onChange={files => this.onChange(files)} />
//Pass arrow function as props
<TestComponent click={() => this.onChange()}
// To avoid above issue, re-write the code as follows,
class TestComponent extends React.Component {
    constructor(props) {
        super(props);
        this.onChange = this.onChange.bind(this);
    }
    render() {
        <input type="text" onChange={this.onChange} />
    }
}

Virtualizing the long lists

Throttling and Debouncing

Throttling and Debouncing are important optimization concepts to avoid multiple API calls.

Please read the article Throttling And Debouncing Using JavaScript, to learn more about Throttling and Debouncing with examples.

Summary

  1. Use React.Fragment instead of "div" to wrap the elements.
  2. Use Production Build in Webpack by setting mode = production.
  3. Use Lazy loading components in React.
  4. Implement shouldComponentUpdate() to avoid unwanted renders.
  5. Use React.PureComponent to avoid unwanted renders.
  6. Use Memoization for functional compoenents.
  7. Use Functional Components.
  8. Avoid the arrow function inside the render() function.
  9. Virtualizing the long lists using the "windowing" concept.
  10. Implement Throttling and Debouncing to avoid multiple API calls.

Other than the above, many optimization techniques are available. Learn all the techniques before developing the React applications. It will help to deliver the application with better quality. I hope you liked it and learned about optimization techniques in React applications.