[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.memosees “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 |

