React performance optimization is not about making every component memoized. The goal is to reduce expensive or unnecessary work when there is a measurable reason to do so. React.memo, useMemo, useCallback, state placement, component boundaries, and profiling are tools that solve different problems.
What is React.memo?
React.memo creates a memoized version of a function component. When the parent renders, React can skip rendering the memoized child if its props are considered equal to the previous props.
const UserCard = React.memo(function UserCard({ name }) {
return <div>{name}</div>;
});
How the default comparison works
By default, React.memo uses a shallow comparison of props. Primitive values such as strings and numbers are compared by value, while objects, arrays, and functions are compared by reference.
const a = { role: "admin" };
const b = { role: "admin" };
console.log(a === b); // false
React.memo with stable primitive props
const UserRow = React.memo(function UserRow({ name, age }) {
console.log("UserRow rendered");
return <div>{name} - {age}</div>;
});If the parent changes unrelated state and these primitive props remain the same, React.memo can skip the child render.
React.memo with object props
function Parent() {
const [count, setCount] = useState(0);
const user = {
name: "Ravi",
role: "Developer"
};
return <UserCard user={user} />;
}The user object is recreated on every parent render. A memoized child sees a new reference even when the object contains the same values.
Using useMemo with React.memo
const user = useMemo(() => ({
name: "Ravi",
role: "Developer"
}), []);
return <UserCard user={user} />;This can stabilize the object reference. Use it when reference stability actually matters, rather than automatically memoizing every object.
Using useCallback with React.memo
const handleSelect = useCallback((id) => {
setSelectedId(id);
}, []);
return <UserList onSelect={handleSelect} />;A new function normally has a new reference on every render. useCallback can preserve the function reference when its dependencies have not changed.
Custom comparison
const UserCard = React.memo(
function UserCard({ user }) {
return <div>{user.name}</div>;
},
(previousProps, nextProps) => {
return previousProps.user.id === nextProps.user.id;
}
);A custom comparison should be used carefully. A comparison that is more expensive than the render itself can make performance worse.
State placement is often more important
Before adding memoization, consider whether state is located at the correct level. If a rapidly changing value only affects a small part of the page, keeping that state close to the affected component can prevent unrelated components from participating in updates.
Avoid premature optimization
Memoization adds complexity. It should be based on an actual performance need, especially for expensive components, large lists, or frequently changing parents. Small components usually do not need aggressive memoization.
Profiling React applications
React DevTools Profiler can show which components rendered and how much rendering work occurred. Use profiling to identify the expensive path before choosing React.memo, useMemo, useCallback, virtualization, or a different state architecture.
flowchart TD
A[Performance issue] --> B[Profile]
B --> C[Find expensive work]
C --> D[Reduce unnecessary state updates]
C --> E[Stabilize references]
C --> F[Memoize expensive components]
D --> G[Measure again]
E --> G
F --> G
Common performance problems
- Rendering very large lists without virtualization.
- Keeping frequently changing state too high in the tree.
- Passing new object and function references to memoized children.
- Doing expensive calculations during every render.
- Running effects more often than necessary.
React.memo vs useMemo vs useCallback
| Tool | What it memoizes | Typical purpose |
|---|---|---|
| React.memo | Component rendering based on props | Skip unnecessary child renders |
| useMemo | Calculated value | Avoid repeated expensive calculation or stabilize a value reference |
| useCallback | Function reference | Stabilize callback identity |
Interview questions
- What does React.memo do?
- Why can an object prop defeat memoization?
- When would you use useCallback with React.memo?
- What is premature optimization?
- How do you investigate a React performance problem?
- Can React.memo make an application slower?
Key takeaways
- Measure first.
- React.memo is useful when repeated child rendering is actually expensive.
- Reference identity matters for objects, arrays, and functions.
- State placement can eliminate more work than memoization.
- Use the simplest optimization that solves the measured problem.