How to stop React components from re-rendering
Published: 2023-06-04 18:14:22

by [Taofiq Aiyelabegan](https://blog.openreplay.com/authors/taofiq-aiyelabegan)

<blockquote><em>React's re-rendering process is necessary to keep the user interface up-to-date with the latest data, but it can also be a performance bottleneck. If components re-render too frequently, it can slow down the application and make it feel unresponsive. This article will show several ways to avoid needless re-rendering for a smoother, faster user interface.</em></blockquote>

React rendering refers to the process of updating the user interface (UI) of a React application to show the changes in its underlying state or props. When a component's state or props change, React automatically re-renders the component, i.e., it calls the component's render method again to generate a new representation of the user interface.

In this article, we will explain how to use the [`useMemo`](https://react.dev/reference/react/useMemo), [`useCallback`](https://react.dev/reference/react/useCallback), and [`useRef`](https://react.dev/reference/react/useRef) hook to optimize expensive calculations, avoid unnecessary re-renders, and store values that don't need to trigger re-renders. By the end of this article, you'll better understand how to improve your React applications' performance using these powerful React hooks.

## Using React.useMemo

The `useMemo` hook can prevent unnecessary re-renders by memoizing the result of a function and only recomputing it if its dependencies have changed.

Let's take a look at the example below:

```javascript
import React, { useState, useMemo } from 'react';

function calculateTotal(items) {
  console.log('calculating total');
  return items.reduce((total, item) => total + item.value, 0);
}
function App() {
  const [items, setItems] = useState([
    { id: 1, value: 10 },
    { id: 2, value: 20 },
    { id: 3, value: 30 },
  ]);
  const total = useMemo(() => calculateTotal(items), [items]);
  function handleClick() {
    setItems([
      { id: 1, value: 10 },
      { id: 2, value: 20 },
      { id: 3, value: 40 },
    ]);
  }
  return (
    <div>
      <p>Total: {total}</p>
      <button onClick={handleClick}>Update</button>
    </div>
  );
}
```

From the above example, we initialize a state named `items`, representing an array of objects with id and value properties. The `calculateTotal` function is used to sum up the values of all the items in the array.

The `total` variable is computed using `useMemo`, which takes a function that returns the memoized value, and an array of dependencies. In this case, we only pass `items` as the array. The memoized value is only recalculated if any of the dependencies have changed. In this case, the only dependency is the items array, so `calculateTotal` will only be called again if the items array has changed.

When the `handleClick` function is called, it updates the items state to include a new item with a value of 40. Say we did not use the `useMemo` hook; the entire component would re-render, even though the total value hasn't changed. But because we're using `useMemo`, `calculateTotal` is only called once again, resulting in better performance.

`useMemo` is a useful hook for optimizing performance in React by preventing unnecessary re-renders.

**NB**: Memoization should only be used for expensive calculations, as it adds some overhead to the rendering process.


## React.useRef

The second way to optimize performance in React applications by preventing unnecessary re-renders is using the `useRef` hook, which can prevent unnecessary re-renders by storing a mutable value that persists across renders.

Below is an example of how `useRef` can be used to prevent unnecessary re-renders:

```javascript
import React, { useRef, useState } from 'react';

function App() {
  const [name, setName] = useState('');
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }
  return (
    <div>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        ref={inputRef}
      />
      <button onClick={handleClick}>Focus</button>
    </div>
  );
}
```


Here, we have a state stored as `name`, which stores the value of an input field. The `inputRef` variable is created using the `useRef` hook, which returns a mutable ref object that can be used to store any value.

The `inputRef` ref object is then passed to the input element as a ref prop. This allows us to directly access the underlying DOM node using `inputRef.current`.

In the `handleClick` function, we use `inputRef.current.focus()` to programmatically focus the input field when the button is clicked.

If we did not use the `useRef` hook to store the `inputRef` value, we would have to re-render the entire component every time the `handleClick` function is called just to get access to the input field. But because we're using useRef, the inputRef value persists across renders, preventing unnecessary re-renders and improving performance.

Also, `useRef` should only be used for mutable values that don't affect the component's rendering. If a mutable value does affect the rendering of the component, it should be stored in the state instead.

<div style="background-color:#efefef; border-radius:8px; padding:10px; display:block;">
<hr/>
<h3><em>Session Replay for Developers</em></h3> 
<p><em>Uncover frustrations, understand bugs and fix slowdowns like never before with <strong><a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a></strong> — an open-source session replay suite for developers. It can be <strong>self-hosted</strong> in minutes, giving you complete control over your customer data.</em></p>
<img alt="OpenReplay" style="margin-top:5px; margin-bottom:5px;" width="768" height="400" src="https://raw.githubusercontent.com/openreplay/openreplay/main/static/openreplay-git-hero.svg" class="astro-UXNKDZ4E" loading="lazy" decoding="async">
<p><em>Happy debugging! <a href="https://openreplay.com" target="_blank">Try using OpenReplay today.</a></em><p>
<hr/>
</div>

## React.useCallBack

The `useCallBack` hook memoizes functions so they are not recreated on each component render. When memoized with `useCallback`, a function will only be created once and reused on subsequent renders if the function's dependencies remain the same.

Let's look at an example of how the `useCallBack` hook can help prevent unnecessary re-renders.

```javascript
import React, { useState } from 'react';

function App({ items }) {
  const [visibleItems, setVisibleItems] = useState([]);
  const toggleItemVisibility = (itemId) => {
    if (visibleItems.includes(itemId)) {
      setVisibleItems(visibleItems.filter((id) => id !== itemId));
    } else {
      setVisibleItems([...visibleItems, itemId]);
    }
  };
  return (
    <div>
      {items.map((item) => (
        <div key={item.id}>
          <h2>{item.title}</h2>
          <button onClick={() => toggleItemVisibility(item.id)}>
            {visibleItems.includes(item.id) ? 'Hide' : 'Show'}
          </button>
          {visibleItems.includes(item.id) && <p>{item.content}</p>}
        </div>
      ))}
    </div>
  );
}
```
Assuming we have the above component that shows the list of items, users can toggle the visibility of each item displayed. The `toggleItemVisibility` function is created inside the component and passed as a callback to each button's `onClick` event. However, since `toggleItemVisibility` depends on the state variable `visibleItems`, it will be recreated on every render, causing unnecessary component re-renders.

To prevent this, we can make use of the `useCallBack` hook by memoizing the `toggleItemVisibility` function like below:

```javascript
import React, { useState, useCallback } from 'react';

function ItemList({ items }) {
  const [visibleItems, setVisibleItems] = useState([]);
  const toggleItemVisibility = useCallback((itemId) => {
    if (visibleItems.includes(itemId)) {
      setVisibleItems(visibleItems.filter((id) => id !== itemId));
    } else {
      setVisibleItems([...visibleItems, itemId]);
    }
  }, [visibleItems]);
  return (
    <div>
      {items.map((item) => (
        <div key={item.id}>
          <h2>{item.title}</h2>
          <button onClick={() => toggleItemVisibility(item.id)}>
            {visibleItems.includes(item.id) ? 'Hide' : 'Show'}
          </button>
          {visibleItems.includes(item.id) && <p>{item.content}</p>}
        </div>
      ))}
    </div>
  );
}
```

Here, the `visibleItems` state is being passed as the second argument to the `useCallback` hook,ensuring that the `toggleItemVisibility` function is only recreated when the `visibleItems` state variable changes. This prevents unnecessary re-renders of the component and improves performance.


## Conclusion

In this tutorial, we have learned what React re-rendering is, how it can cause bottlenecks in React applications, and ways of optimizing unnecessary re-renders. Generally, React provides several hooks that can be used to optimize the performance of a React application. These hooks provide a way to store values and functions between renders, which can significantly improve the performance of a React application.