
Part 2: Optimizing React Re-renders — Memoization and Smarter Components
July 15, 2025
Ashish Gogula

July 15, 2025
Ashish Gogula
In Part 1, we talked about why React re-renders and what triggers it.
If you haven’t read it yet, you can check it out here: React Rendering Demystified — What Actually Causes Re-Renders (opens in new tab)
Now that you understand the “why,” let’s go one step further — how do we stop unnecessary re-renders from slowing down our apps?

This post is all about simple ways to make React faster by telling it:
"Hey, nothing changed here — don’t bother re-rendering this part."
You don’t need to over-optimize or memorize everything. Just get the basics right, and React will take care of the rest.
Imagine you have a child component that always gets the same props. But every time the parent changes, the child still re-renders — even though nothing changed for it.
That’s where React.memo comes in. It tells React:
"Hey, if the props are exactly the same, don’t re-run this component. Just reuse the last one."
Here’s how it works:
const Child = React.memo(function Child({ label }) {
console.log("Child rendered");
return <p>{label}</p>;
});
Now, unless label changes, React will skip rendering Child.
Use it for:
Every time a component re-renders, it also remakes any functions inside it. That’s usually fine — unless you're passing those functions as props to memoized children.
Why? Because even if the function does the same thing, its reference changes, and that makes React think something changed.
useCallback helps by giving you the same function reference between renders
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []);
Use it when:
If you're doing something slow — like sorting a big array or filtering data — React will do it again every time your component re-renders. That’s wasted work.
useMemo helps you remember the result of a calculation until its inputs change.
const sortedData = useMemo(() => {
return expensiveSort(data);
}, [data]);
Now expensiveSort() only runs when data changes — not on every render.
Use it for:
Let’s say your Parent component updates often, but your HeavyChild doesn’t need to care. If everything is in one tree, React will keep re-rendering the whole thing.
The fix? Split it.
function Parent() {
return (
<>
<LightUI />
<HeavyUI />
</>
);
}
Now you can memoize just HeavyUI, and it won’t be touched when LightUI updates.
Use this strategy when:
Here are a few traps to avoid:
Memoization is a tool — not a magic spell.
React is already fast. You don’t need to “optimize” every component.
Don’t use memo, useCallback, or useMemo if:
Sometimes, plain React is better than trying to be too clever.
Before we wrap up, let’s see a quick example of everything in action.
import React, { useState, useCallback } from 'react';
// Child component
const Child = React.memo(({ onClick }) => {
console.log('Child rendered');
return (
<button onClick={onClick}>Click from Child</button>
);
});
// Parent component
export default function Parent() {
const [parentClicks, setParentClicks] = useState(0);
const [childClicks, setChildClicks] = useState(0);
const handleChildClick = useCallback(() => {
setChildClicks(prev => prev + 1);
}, []);
console.log('Parent rendered');
return (
<div>
<h2>Parent Clicks: {parentClicks}</h2>
<h3>Child Clicks: {childClicks}</h3>
<button onClick={() => setParentClicks(c => c + 1)}>
Re-render Parent
</button>
<Child onClick={handleChildClick} />
</div>
);
}
Child is wrapped in React.memo so it won’t re-render unless its props change.
handleChildClick is wrapped in useCallback to avoid passing a new function on every render.
Clicking the "Re-render Parent" button updates only the parent — the child does not re-render.
Clicking the "Click from Child" button only affects the child’s state.
In the final part, I’ll cover how to actually measure performance, using tools like:
So you can stop guessing — and start fixing the real bottlenecks.