What is React.memo, when to use and difference between React.memo vs useMemo hook

Mar 11, 2023

Imagine you have a toy box filled with different kinds of toys – cars, dolls, balls, and blocks. Every time you want to play with a toy, you have to search through the entire box to find the one you want. This takes a lot of time and effort, especially if you have a lot of toys.

Now, let’s say you have a special shelf for your favorite toy – a red car. Every time you want to play with the red car, you don’t have to search through the entire toy box. Instead, you can just go straight to the shelf and pick it up. This saves you time and effort.

In a way, React.memo is like that special shelf for your favorite toy. It’s a way to optimize your React components by caching the result of their rendering.

In React, re-rendering is a process of updating the UI to reflect any changes made in the component’s state or props. However, sometimes, certain changes in props or state may not require re-rendering, resulting in unnecessary wastage of resources and affecting the application’s performance.

One way to optimize this is to memoize expensive calculations using React.memo or useMemo. Memoization is a technique that stores the result of a function and returns it when the same inputs are provided, instead of recalculating it every time the function is called with the same inputs.

React.memo is a Higher Order Component (HOC) that wraps a component and returns a new component that will only re-render if its props change. React.memo checks if the new props are different from the previous props using a shallow comparison. If they are the same, React.memo will use the previously calculated result and prevent the re-rendering of the component.

When you use React.memo on a component, React will check if the props passed to the component have changed since the last time it was rendered. If the props haven’t changed, React will skip rendering the component and use the cached result instead. This can improve the performance of your application by reducing the number of unnecessary renders.

To explain this concept, we can create two functional components – a parent and a child. The parent component will have two states: parentCount and childCount, childCount will be passed to the child component as a prop.

Normally, when the state of the parent component changes, both the parent and child components will re-render. However, we can optimize the rendering of the child component by using memoization. Since the child component only depends on the childCount prop, we can prevent unnecessary re-renders by using React.memo. This means that the child component will only re-render when the childCount prop changes, rather than every time the parent component updates.

Let’s understand it by writing some code

Below is code for parent component

import { useState } from "react";
import ChildComponent from "./Child";

const Parent = (props) => {
    const [childCount, setChildCount] = useState(0);
    const [parentCount, setParentCount] = useState(0);
    console.log("rendering ParentComponent", parentCount);
    const handleParentCount = () => {
 	setParentCount(parentCount + 1);
    };

    const handleChildCount = () => {
	setChildCount(childCount + 1);
    };

    const handleReset = () => {
	setChildCount(0);
	setParentCount(0);
	console.clear();
    };

    return (
        <div>
	  <button onClick={handleChildCount}>Increment child state</button> <br />
	  <br />
	  <button onClick={handleParentCount}>Increment parent state</button>
	  <br />
	  <br />
	  <button onClick={handleReset}>Reset</button>
	  <ChildComponent count={childCount} />
	</div>
   );
};

export default Parent;

Child Component without memoization

import React from "react";

const ChildComponent = ({ count }) => {
    console.log("rendering ChildComponent", count);
    return (
        <div>
	    <p>Count from parent: {count}</p>
	</div>
    );
};

export default ChildComponent;

The parent component will display three buttons on browser

Increment child state – click on this will change the childCount state and that will re-render parent and child both components. See the console, you will find that both components render:

Increment parent state – clicking on this will change the parentCount state and that will re-render parent and child both components because child component is not wrapped in React.memo HOC

See the console, and you will find that both components render, although only parentCount state changes.

Reset – clicking on this will reset both state thus both components will re-render

Now, let’s update the child component to memoize the component.

import React from "react";

const ChildComponent = ({ count }) => {
    console.log("rendering ChildComponent", count);
    return (
        <div>
	    <p>Count from parent: {count}</p>
	</div>
    );
};

export default React.memo(ChildComponent);

Now, again see what does console.log prints on each button specially when childCount state does not change.

Increment child state – clicking on this will change the childCount state and that will re-render parent and child both components. See the console, you will find that both components render.

It has same behavior as we have seen it without memoization, it’s because it changes the childCount state and memoized child component depends on this prop.

Increment parent state – You will notice the difference here.

Clicking on this will change the parentCount state and that will re-render only parent component. See the console:

Here are some common scenarios where you might want to use React.memo

  1. Pure functional components that receive props and render UI based on those props.
  2. Components that render large amounts of data or complex UI structures.
  3. Components that perform expensive calculations or use complex logic to determine their output.
  4. Components that rely on props that rarely change over time.
  5. Components that are used frequently throughout your application.
  6. Components that trigger re-renders frequently due to changes in state or prop values.

Here are some scenarios where you might not want to use React.memo

It’s important to remember that memoization is not a silver bullet for performance optimization. While it can be helpful in certain scenarios, it’s not always necessary or beneficial to use. As with any performance optimization technique, it’s important to measure and test the impact of memoization on your application’s performance before implementing it.

Here are some scenarios where you might not want to use React.memo:

  1. Components that always need to be re-rendered, even if the props haven’t changed. For example, a component that contains a timer that needs to update every second.
  2. Components that have their own internal state that changes frequently. Memoizing a component that relies heavily on its own state could actually hurt performance, as it could prevent necessary updates from occurring.
  3. Components that rely on context or other global state that could change frequently. Memoizing a component that relies on this kind of state could also prevent necessary updates from occurring.
  4. Components that are only used once or very infrequently. Memoizing a component that isn’t used very often could be unnecessary and add unnecessary complexity to your code.

React.memo vs useMemo

React.memo and useMemo are both used for optimizing React component performance, but they serve different purposes.

React.memo is a higher-order component (HOC) that can be used to memoize a functional component, preventing it from re-rendering unnecessarily. React.memo compares the previous and current props of a component, and only re-renders the component if the props have changed. This can be helpful for optimizing components that receive props that don’t change frequently or that have expensive rendering logic.

useMemo, on the other hand, is a hook that can be used to memoize the result of a function. useMemo takes a function and a list of dependencies, and returns a memoized value that only updates when one of the dependencies changes. This can be helpful for optimizing components that perform expensive calculations or data transformations that don’t need to be repeated on every render.

In summary, React.memo is used to optimize the rendering of a component by memoizing it based on its props, while useMemo is used to optimize the performance of a function by memoizing its result based on its dependencies.

Let’s see the example of useMemo:

import { useMemo, useState } from "react";

const ExplainUseMemo = () => {
	const [number, setNumber] = useState(0);
	const [refresh, setRefresh] = useState(false);
	console.log("Component rendered");
	// expensive calculation function
	const calculateValue = (num) => {
		let result = 0;
		for (let i = 1; i <= num; i++) {
			result += i;
		}
		return result;
	};

	const start = performance.now();
	const calculatedValue = calculateValue(number);
	//const calculatedValue = useMemo(() => calculateValue(number), [number]);
	const end = performance.now();

	const executionTime = end - start;
	console.log(`Execution time: ${executionTime} ms `);

	if (executionTime === 0) {
		console.log(`Memoized value returned, value is ${calculatedValue}`);
	} else {
		console.log(`Function calculated new value, value is ${calculatedValue}`);
	}

	const handleClick = () => {
		setNumber(number + 1000);
	};

	const handleRefresh = () => {
		setRefresh(!refresh);
	};

	return (
		<div>
			<p>Component rendered</p>
			<p>Number: {number}</p>
			<p>Calculated Value: {calculatedValue}</p>
			<button onClick={handleClick}>Increment Number</button>
			<br /> <br />
			<button onClick={handleRefresh}>Refresh component</button>
		</div>
	);
};

export default ExplainUseMemo;

In this example, we have a component ExplainUseMemo that renders a number and a calculated value based on that number. The calculateValue function performs an expensive calculation, adding up all the numbers from 1 to the passed-in number.

By using useMemo, we can memoize the result of calculateValue and only re-calculate it when the number state changes. This prevents the function from being re-run unnecessarily on every render.

Let’s give a look at console log to evaluate the impact. Running the above component will show two button in browser. One will increment the number and update the number state which would be received by calculateValue function to do the expensive calculation. Another is used to refresh the component to check how much execution time calculateValue function takes without useMemo or with useMemo.

Let’s first see how it behaves without useMemo

You would first have to comment the useMemo statement in above code snippet and execute the function without useMemo.

Let’s examine the statement that is being outputted in the console log:

I first clicked on Increment Number button and that logged top three lines. It rendered the component, execute the calculateValue function, log the execution time.

Next, I clicked twice on Refresh component button, that simple toggle the refersh state so we can ensure that it re-render the component. You will notice that every time it executes the calculateValue function. Whether we wants to execute the calculateValue function only when number state change.

Now, we will execute the code snipper with useMemo statement and will see if it calls the calculateValue function on click of refresh button.

Here is the console log with useMemo statement:

You must have noticed that calculateValue function only being called once. Clicking on Refresh component rendered the component but it does not actually execute the calculateValue function because number state did not change.

Conclusion

Understanding the differences and appropriate use cases for React.memo and useMemo can greatly improve the performance of your React applications. By memoizing components and expensive computations, we can minimize unnecessary re-renders and improve the overall efficiency of our code. While these optimizations may seem small, they can make a big difference in the performance of large and complex applications. Therefore, it is important to carefully consider when to use React.memo and useMemo to achieve optimal performance and user experience.