Understanding React.memo: When, Why, and How to Use It

Ram Kumar

Ram Kumar

January 2, 20254 min read

Understanding React.memo: When, Why, and How to Use It

React's ecosystem offers a plethora of tools to help developers optimize the performance of their applications. One of these tools is React.memo, a higher-order component (HOC) that can significantly reduce unnecessary re-renders. In this post, we'll dive into what React.memo is, why you should use it, how it works, and tips for testing your components' performance.

What Is React.memo?

React.memo is a higher-order component that memoizes a functional component's output. It ensures that the component only re-renders if its props have changed. Essentially, it wraps your functional component and prevents React from re-rendering it unless there's a difference in the incoming props.

Syntax

Here's a simple example of React.memo:

import React from 'react';

type MyComponentProps = {
  value: string;
};

const MyComponent: React.FC<MyComponentProps> = React.memo((props) => {
  console.log('Rendering MyComponent');
  return <div>{props.value}</div>;
});

export default MyComponent;

In this example, MyComponent will only re-render if the value prop changes.

When to Use React.memo

While React.memo is a powerful tool, it’s not a silver bullet for every performance issue. Here are some situations where it makes sense to use it:

1. Components with Expensive Calculations or Complex JSX

If a component involves heavy computation or renders a complex tree, avoiding unnecessary re-renders can save resources.

2. Components That Rarely Update

If a component’s props or state change infrequently, React.memo can prevent needless re-renders.

3. Large Lists or Grids

In scenarios like rendering large lists or grids, optimizing individual list items with React.memo can improve overall performance.

4. Child Components in Parent Re-Renders

When a parent component re-renders due to state or prop changes, its child components may re-render unnecessarily. Wrapping child components in React.memo can mitigate this.

How to Use React.memo

Using React.memo is straightforward. You wrap your functional component like so:

const MemoizedComponent = React.memo(Component);

Custom Comparison Function

By default, React.memo performs a shallow comparison of props. If you need a more specific check, you can provide a custom comparison function:

type MyComponentProps = {
  value: string;
};

const MemoizedComponent = React.memo(
  (props: MyComponentProps) => {
    return <div>{props.value}</div>;
  },
  (prevProps, nextProps) => {
    return prevProps.value === nextProps.value;
  }
);

In the example above, the second argument to React.memo is a function that compares prevProps and nextProps and returns true if the props are the same, preventing a re-render.

Why Use React.memo?

The primary purpose of React.memo is performance optimization. By skipping unnecessary renders, it helps:

Reduce CPU Usage: Avoiding unnecessary render cycles reduces the computational load on the browser.

Improve Responsiveness: Applications feel smoother and more responsive.

Lower Memory Usage: Since fewer components are rendered, memory consumption is reduced.

However, you should balance the performance gain against the additional complexity and potential overhead of memoization. If your props are inexpensive to compute or the component updates frequently, memoization might not yield significant benefits.

How React.memo Helps Performance Optimization

Shallow Prop Comparison

React.memo uses a shallow comparison by default. For primitive types like strings, numbers, and booleans, this works well. However, for objects, arrays, and functions, shallow comparison only checks if the reference is the same, not the contents. This is why passing immutable data or memoized functions is important.

Works Well with useCallback and useMemo

To maximize the effectiveness of React.memo, pair it with useCallback or useMemo to ensure that references don’t change unnecessarily:

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

type ChildProps = {
  onClick: () => void;
};

const Child: React.FC<ChildProps> = React.memo(({ onClick }) => {
  console.log('Rendering Child');
  return <button onClick={onClick}>Click me</button>;
});

type ParentProps = {};

const Parent: React.FC<ParentProps> = () => {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    console.log('Button clicked');
  }, []);

  return (
    <div>
      <Child onClick={handleClick} />
      <button onClick={() => setCount(count + 1)}>Increment Parent Count</button>
    </div>
  );
};

export default Parent;

In this example, useCallback ensures that the onClick function reference remains the same, allowing React.memo to skip re-rendering the Child component.

Testing Component Performance

1. React Developer Tools

The React DevTools Profiler is invaluable for measuring component performance. It shows:

  • How often components render.
  • Why they re-rendered (e.g., due to prop or state changes).

To use the Profiler:

Open the React DevTools in your browser.

Navigate to the "Profiler" tab.

Record performance while interacting with your app.

2. Console Logging

You can add console.log statements in your components to track when they render. For instance:

type MyComponentProps = {
  value: string;
};

const MyComponent: React.FC<MyComponentProps> = React.memo((props) => {
  console.log('Rendering MyComponent');
  return <div>{props.value}</div>;
});

3. Why Did You Render

The why-did-you-render library helps identify unnecessary renders. It works well with React.memo to ensure your optimizations are effective.

Installation:

npm install @welldone-software/why-did-you-render

Usage:

import React from 'react';
import whyDidYouRender from '@welldone-software/why-did-you-render';

whyDidYouRender(React);

type MyComponentProps = {
  value: string;
};

const MyComponent: React.FC<MyComponentProps> = React.memo((props) => {
  return <div>{props.value}</div>;
});

MyComponent.whyDidYouRender = true;
export default MyComponent;

Conclusion

React.memo is a powerful tool for optimizing React applications, but like any tool, it’s important to use it judiciously. It’s best suited for components that render frequently with the same props or for computationally expensive components.

By understanding when and how to use React.memo, and combining it with tools like useCallback and performance testing, you can create more efficient React applications. Remember, premature optimization can add unnecessary complexity, so always profile your app before applying optimizations.

Previous: Building a Design System for Enterprise Applications
Next: The TypeScript Feature That Changed How I Write React