Back

Memoization in React

MD Rashid Hussain
MD Rashid Hussain
Feb-2025  -  6 minutes to read
Image source attributed to: https://kinsta.com

Memoisation is a technique used to optimise the performance of a function by caching its results. This is particularly useful when the function is computationally expensive and is called multiple times with the same arguments. Rendering React components is a good example of a computationally expensive operation that can benefit from memoisation.

In this article, we will explore how memoisation can be used in React to optimise the performance of functional components.

React provides a React.memo function that can be used to memoise functional components. When a component is memoised, React will only re-render the component if its props have changed.

import { useState, memo } from 'react';
 
function ParentComponent() {
  console.log('Rendering ParentComponent');
  const [count, setCount] = useState(0);
  const items = ['Item 1', 'Item 2', 'Item 3'];
 
  return (
    <div>
      <div>Count: {count}</div>
      <button onClick={() => setCount((prev) => prev + 1)}>Increment</button>
 
      <MemoisedComponent items={items} />
    </div>
  );
}
 
function ItemList(props: { items: string[] }) {
  console.log('Rendering ItemList');
  return (
    <ul>
      {props.items.map((item) => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  );
}
 
const MemoisedComponent = memo(ItemList);
export default ParentComponent;

When a component is memoised using React.memo, React will compare the previous props with the new props to determine if the component needs to be re-rendered. React performs a shallow comparison of the props by comparing the prop values using the Object.is function. This means, two values are the same if both point to the same reference/location in memory.

  • React.memo relies on javascript's referential equality. Objects, arrays and functions are not compared by their contents but their references. So, every time the ParentComponent is re-rendered, the items array is re-created, leading to a re-render of the MemoisedComponent even though the contents of the array have not changed.
  • Even if we do <MemoisedComponent items={["Item1", "Item2", "Item3"]} />, the items array is re-created every time the ParentComponent is re-rendered, leading to a re-render of the MemoisedComponent.
  • If we have children inside a memoised component, the children will be re-rendered every time the parent is re-rendered, even if the parent is memoised.
  • Same is the case with inline functions like onClick={() => console.log("Hello")}
  • Same is the case with passing inline styles like style={{ color: "red" }}

We can get around all of these issues using stable props.

Stable Props are props that do not change between re-renders. Examples include:

  • Functions/objects defined outside the component.
  • using useMemo to memoise variables/objects.
  • using useCallback to memoise functions.

React provides a useMemo hook that can be used to memoise variables or objects. This is useful when the value of a variable or object is expensive to compute and does not change frequently.

import { useState, useMemo, memo } from 'react';
 
function ParentComponent() {
  console.log('Rendering ParentComponent');
  const [count, setCount] = useState(0);
 
  const memoisedItems = useMemo(() => ['Item 1', 'Item 2', 'Item 3'], []);
 
  return (
    <div>
      <div>Count: {count}</div>
      <button onClick={() => setCount((prev) => prev + 1)}>Increment</button>
 
      <ItemList items={memoisedItems} />
    </div>
  );
}
 
function ItemList(props: { items: string[] }) {
  console.log('Rendering ItemList');
  return (
    <ul>
      {props.items.map((item) => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  );
}
 
const MemoisedComponent = memo(ItemList);
export default ParentComponent;

To prevent unnecessary re-renders, we can use the useMemo hook to keep the reference stable. The useMemo hook takes a function that returns the value to memoise and an array of dependencies. If any of the dependencies change, the function is re-executed to compute the new value.

In the example above, the memoisedItems variable is memoised using the useMemo hook. The useMemo hook takes a function that returns an array of items and an empty array as dependencies. Since the dependencies array is empty, the function is only executed once when the component is mounted.

React provides a useCallback hook that can be used to memoise functions. This is useful when a function is expensive to compute and does not change frequently.

import { useState, useCallback, memo } from 'react';
 
function ParentComponent() {
  console.log('Rendering ParentComponent');
  const [count, setCount] = useState(0);
 
  const memoisedItems = useMemo(() => ['Item 1', 'Item 2', 'Item 3'], []);
 
  const onItemClick = useCallback((item) => {
    console.log(`Clicked on ${item}`);
  }, []);
 
  return (
    <div>
      <div>Count: {count}</div>
      <button onClick={() => setCount((prev) => prev + 1)}>Increment</button>
 
      <ItemList items={memoisedItems} fn={onItemClick} />
    </div>
  );
}
 
function ItemList(props: { items: string[], fn: (item: string) => void }) {
  console.log('Rendering ItemList');
  return (
    <ul>
      {props.items.map((item) => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  );
}
 
const MemoisedComponent = memo(ItemList);
export default ParentComponent;

To make the functions stable, we can use the useCallback hook. The useCallback hook takes a function and an array of dependencies. If any of the dependencies change, the function (and its reference) is created again.

In the example above, the onItemClick function is memoised using the useCallback hook. The useCallback hook takes a function that logs the clicked item and an empty array as dependencies. Since the dependencies array is empty, the function is only created once when the component is mounted.

useMemo and useCallback are very similar in their usage. As a thumb rule

  • Use the useMemo hook, if you want to memoise variables/objects. (Remembers the value)
  • Use the useCallback hook, if you want to memoise functions. (Remembers the function itself)

Memoisation is a powerful technique that can be used to optimise the performance of React components. By memoising variables, objects, and functions, we can prevent unnecessary re-renders and improve the overall performance of our application. But remember, memoisation is not a silver bullet and should be used judiciously to avoid unnecessary complexity.

React 19 has eliminated the need for memoisation in most cases by introducing Concurrent Mode and automatic batching. However, memoisation can still be useful in certain scenarios, especially when working with legacy code or third-party libraries.