Back to list
ReactHookuseMemoReact.memoPerformance

[React][Hook] Distinguish useMemo and React.memo for Render Optimization

useMemo caches values; React.memo skips child re-renders. Learn how they differ, and why object/array props need useMemo to keep React.memo working.

1. Environment

  • OS: Windows 11 / macOS
  • Frontend library: React.js (v18+)
  • Core concept: Render optimization (useMemo, React.memo)
  • IDE: Visual Studio Code (VS Code)

2. Problem

One of React?’s default rules is: when a parent component?’s state changes, that component and its children re-run (re-render). In real apps that can waste work in two ways:

  • Wasted values: Heavy math or large array filters run again on every state change.
  • Wasted components: Typing one character in a search box re-draws a heavy child with 1,000 items even when that child?’s props did not change.

While learning performance, you will meet both useMemo and React.memo. Both names include “memo,|–so they look similar - but they are different tools and should not be mixed up.

Related: useMemo and memoization

3. Cause

The key difference is what each one caches.

  • useMemo (cache a value): A Hook that stores the result of a calculation (variable, object, array, etc.) and reuses it.
  • React.memo (cache a component): A higher-order component (HOC) that wraps a component. If the parent re-renders but the child?’s props are unchanged, the child skips rendering and reuses the previous UI.

4. Solution

1) Problem: an unrelated child re-renders

Clicking the count button should only update the parent - but Child re-renders every time too.

// Child re-renders even when it should not
const Child = ({ title }) => {
  console.log('Child rendered!');
  return <div>{title}</div>;
};

function Parent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Count: {count}</button>
      <Child title="Static text" />
    </div>
  );
}

2) Protect the child with React.memo

Wrap the child in React.memo. As long as title stays the same, changing count a hundred times will not re-render Child.

const Child = React.memo(({ title }) => {
  console.log('Child rendered!');
  return <div>{title}</div>;
});

3) The React.memo trap - and fixing it with useMemo

What if the parent passes an array or object instead of a string?

  • On every parent render a new array/object is created, so the memory reference changes.
  • React.memo sees “props changed|–and drops its protection.

Stabilize the value with useMemo:

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

// 1. Protect the component with React.memo
const Child = React.memo(({ userList }) => {
  console.log('Child rendered!');
  return <div>User count: {userList.length}</div>;
});

function Parent() {
  const [count, setCount] = useState(0);

  // Trap: a bare array gets a new reference on every parent render
  // const users = ['Alice', 'Bob', 'Charlie'];

  // 2. Protect the array (value) with useMemo - keep the same reference
  const users = useMemo(() => {
    return ['Alice', 'Bob', 'Charlie'];
  }, []); // empty deps |--create once

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Count: {count}</button>
      {/* Child will not re-render no matter how often count changes */}
      <Child userList={users} />
    </div>
  );
}

5. Summary

Similar names, different jobs:

useMemo React.memo
What it caches A value (result, array, object) A component (UI output)
When it refreshes When a dependency in [] changes When received props change
Form Hook: useMemo(() => {}, []) HOC: React.memo(Component)
In practice Skip heavy work; stabilize references Stop useless child re-renders

Related posts