
In the world of React, every time a parent component’s state changes, React’s default behavior is to re-render every single one of its children.
In small apps, this is fine. In large apps, this leads to “laggy” interfaces.
Today, we’ll look at how to stop unnecessary renders using your provided code as a master blueprint.
The Problem: The “Default” Render Behavior
Imagine clicking a “Counter” button. The number goes up, but your huge list of 1,000 items also refreshes—even though the list didn’t change!
This happens because React recreates everything inside the Parent function on every render, including new instances of your functions.
Step 1: React.memo (The Shield)
The first line of defense is memo. It tells React: “Only re-render this child if its props have actually changed.”
const List = memo(({ items, onItemClick }) => {
console.log("List rendered"); // This will now only log when absolutely necessary
return (
<ul>
{items.map(item => (
<li key={item} onClick={() => onItemClick(item)}>{item}</li>
))}
</ul>
);
});
What happens now?
When the count state in the Parent changes, React looks at the List. It checks the items prop and the onItemClick prop.
If they look the same as last time, it skips the work.
Step 2: useCallback (The Anchor)
Even with memo, the List might still re-render.
Why?
Because of Referential Equality.
In JavaScript, function() {} === function() {} is false.
Every time Parent renders, handleItemClick is a “brand new” function in memory.
To memo, a new function means a changed prop.
We fix this with useCallback:
// Inside Parent component
const handleItemClick = useCallback((item) => {
console.log("Clicked:", item);
}, []); // The empty array ensures this function instance is created ONLY ONCE
useCallback “anchors” the function. It tells React: “Keep this exact same version of the function in memory across all future renders.”
Step 3: The Connection (onItemClick={handleItemClick})
This is where the magic happens. In your JSX, you pass the anchored function to the shielded component:
<List items={items} onItemClick={handleItemClick} />
-
The User Clicks: When a user clicks “Banana”, the
litriggers the anonymous function() => onItemClick(item). -
The Bridge:
onItemClickis just a reference (a pointer) tohandleItemClick. -
The Result: The code in the Parent executes. Because the reference never changed, the
Liststayed perfectly still while thecountupdated.
When Should You Use This?
Don’t wrap everything! Use this “Power Couple” when:
-
The child component (
List) is expensive to render (has lots of elements or complex logic). -
The child component is wrapped in
memo. -
The function is passed as a dependency to a
useEffectinside a child.
Summary Table
| Tool | Role | What it prevents |
React.memo |
Component Wrapper | Re-rendering the child if props stay the same. |
useCallback |
Hook | Re-creating the function instance on every render. |
[] Dependencies |
Guard Rails | Prevents the function from updating unless specific values change. |
Useful links below:
Let me & my team build you a money making website/blog for your business https://bit.ly/tnrwebsite_service
Get Bluehost hosting for as little as $1.99/month (save 75%)…https://bit.ly/3C1fZd2
Best email marketing automation solution on the market! http://www.aweber.com/?373860
Build high converting sales funnels with a few simple clicks of your mouse! https://bit.ly/484YV29
Join my Patreon for one-on-one coaching and help with your coding…https://www.patreon.com/c/TyronneRatcliff
Buy me a coffee https://buymeacoffee.com/tyronneratcliff



