My React Component Kept Re-rendering — What Was Actually Causing It?
Sometimes the hardest React bugs are not caused by a broken component. They are caused by something perfectly normal happening over and over again.
🏪 The Story: A Shopkeeper Who Couldn’t Stop Rearranging the Store
Imagine you own a small shop.
Every time a customer walks through the door, you immediately rearrange every product on every shelf.
The customer looks around and says:
“Wait… I only came inside. Why are you rearranging the entire shop?”
You answer:
“Because something changed.”
But nothing important actually changed. The customer simply walked in.
That is surprisingly similar to what can happen in React.
🔥 The Problem
I had a component that seemed to render again and again. So I added a simple log:
function ProductList() {
console.log("ProductList rendered");
return <div>Products</div>;
}And the console started filling up.
ProductList rendered
ProductList rendered
ProductList rendered
ProductList renderedMy first thought was:
“React is broken. Why is this component rendering so many times?”
That was my first mistake.
🧠 First Question: Is Re-rendering Actually a Problem?
React re-rendering is not automatically a bug.
When state or props change, React may run the component again to figure out what the UI should look like.
Think about the shop again.
If the shop's product list changes, the shopkeeper may need to look at the shelves again. That is normal.
The real problem is not:
“Why did React render?”
The better question is:
“What is causing React to render again, and is that happening more often than necessary?”
🕵️ The Real Bug
Eventually I found something like this:
function App() {
const [count, setCount] = useState(0);
const options = {
limit: 10,
};
return (
<ProductList options={options} />
);
}At first glance, this looked completely harmless.
But there was a hidden detail.
Every time App renders, this object is created again:
const options = {
limit: 10,
};The values look identical, but the object reference is different.
So if the child is doing something like:
useEffect(() => {
fetchProducts(options);
}, [options]);React sees a new options reference and says:
“This dependency changed. Run the effect again.”
🎫 The Ticket Analogy
Imagine a security guard checking tickets.
You give him a ticket that says:
“VIP — Seat 10”
Then five minutes later, you print another ticket with exactly the same text.
The guard does not care that the text is identical. It is a different physical ticket.
JavaScript objects can behave similarly when React compares references.
🔧 The Fix
If the object does not need to be recreated on every render, we can stabilize its reference:
const options = useMemo(() => ({
limit: 10,
}), []);Or, when appropriate, move a constant outside the component:
const OPTIONS = {
limit: 10,
};
function App() {
return <ProductList options={OPTIONS} />;
}But there is an important warning:
Do not throw useMemo or useCallback at every re-render just because you saw a render in the console.
First understand why the render is happening. Then decide whether anything actually needs optimization.
🧪 My Debugging Process
- Put a log inside the component.
Confirm that it is actually rendering again. - Check state updates.
Look forsetStatecalls that may be happening repeatedly. - Check the parent.
A child can render because its parent rendered. - Check props.
Especially objects, arrays, and functions created during render. - Check effects.
An effect that updates state can accidentally create a render loop. - Only then optimize.
First find the cause. Then fix the cause.
💡 What I Learned
The biggest lesson was not a React trick.
It was a debugging mindset.
Don't fight the symptom before understanding the cause.
React was not randomly rendering my component. Something in my component tree was giving React a reason to do its job.
When React renders again, don't immediately ask “How do I stop it?” Ask “Why did it render?”