Fixing Infinite Re-renders When You Pass Object Literals as React Props
Your component re-renders, passes an object literal as a prop, the child re-renders, triggers a useEffect, updates state, and the whole thing starts over. Within milliseconds your browser tab is frozen and the console is screaming Too many re-renders. The culprit is almost always a reference equality problem with an object you created inline.
This is one of those bugs that looks completely innocent in the code but causes catastrophic runtime behavior. Understanding the exact mechanism makes it trivially easy to fix β and to avoid in the future.
What you'll learn
- Why JavaScript reference equality makes inline objects dangerous as props
- How React's re-render cycle turns a single object literal into an infinite loop
- Five concrete fixes ranked by situation: moving objects outside components,
useMemo,useRef, flattening props, andReact.memo - The common mistakes that keep the loop alive even after you think you've fixed it
The Root Cause: Reference Equality in JavaScript
JavaScript compares objects by reference, not by value. Two objects that look identical are not equal unless they point to the same place in memory.
const a = { color: 'red' };
const b = { color: 'red' };
console.log(a === b); // false β different references
console.log(a === a); // true β same reference
This is not a React quirk. It is how JavaScript works at its core. React simply exposes the consequences more dramatically than most code you write day-to-day.
How React Decides to Re-render
When a parent component re-renders, React calls the parent function again from top to bottom. It then compares each prop it is about to pass to a child against the prop it passed on the previous render. The comparison is a strict equality check (===) on the prop value itself.
For primitives like strings and numbers, this is fine. 'red' === 'red' is always true. But for objects, React is comparing references, and a brand-new object literal created inside the render function gets a brand-new reference every single time.
If the child component has a useEffect that lists that object in its dependency array, React sees a new object on every render and fires the effect every render. If that effect sets state, the parent re-renders again β and so it goes, forever. You can read more about how React's development mode adds another layer to this problem in this breakdown of why useEffect fires twice in dev but once in production.
Why Object Literals Are the Silent Culprit
The dangerous pattern looks completely natural, which is why it catches so many developers off guard.
function ParentComponent() {
const [count, setCount] = React.useState(0);
return (
<ChildComponent
config={{ theme: 'dark', size: 'large' }} {/* new object every render */}
onClick={() => setCount(c => c + 1)}
/>
);
}
function ChildComponent({ config, onClick }) {
React.useEffect(() => {
console.log('config changed:', config);
// do something with config...
}, [config]); // fires every render because config is always a new reference
return <button onClick={onClick}>Click</button>;
}
Every time ParentComponent renders β for any reason β it creates a new { theme: 'dark', size: 'large' } object. Even though the values are identical, the reference is new. React dutifully fires the useEffect, and if that effect triggers a state update anywhere up the tree, you have your loop.
The same problem applies to inline arrays ([]), inline functions (() => {}), and inline class instances. Anything that gets constructed fresh inside a render function body.
Spotting the Infinite Loop in Practice
React will throw Error: Too many re-renders. React limits the number of renders to prevent an infinite loop when it detects the problem synchronously. But often the loop is more subtle β the component keeps re-rendering at high frequency without crashing, burning CPU cycles and making the UI sluggish.
To diagnose it, open React DevTools and use the Profiler tab. Enable
Frequently Asked Questions
Why does passing an object as a prop cause a React component to re-render infinitely?
Every time a parent component renders, an object literal written inside the JSX gets a new memory reference, even if the values inside it haven't changed. React compares props with strict equality, so it always sees a 'new' prop and re-renders the child. If the child's useEffect depends on that prop, the effect fires again and can trigger more state updates, creating an infinite loop.
Does wrapping a child in React.memo stop infinite re-renders caused by object props?
Not on its own. React.memo uses shallow equality, which compares object props by reference. If the parent creates a new object literal on every render, React.memo sees a new reference and re-renders the child anyway. You need to also stabilize the object in the parent using useMemo so the reference only changes when the data actually changes.
How do I know if a useMemo dependency array is correct for an object?
List every variable from the component scope that is read inside the useMemo callback. If any of those variables change and your object should reflect that change, they must be in the dependency array. An empty array means the object never updates, which is usually wrong β move a truly static object to module scope instead.
Can inline arrays cause the same infinite re-render problem as inline objects?
Yes. Arrays are objects in JavaScript and are compared by reference, not by value. An inline array like columns={['name', 'email']} creates a new reference on every render and causes the exact same problem. Apply the same fixes: define it at module scope if it's static, or wrap it in useMemo if it depends on component state or props.
What is the fastest way to find which prop is causing an infinite re-render?
Open the React DevTools Profiler, record a session, and look for components that re-render continuously with no user interaction. Click into those renders to see which props changed between renders. The prop showing a different value on every frame β especially an object or function β is the culprit.
π€ Share this article
Sign in to saveRelated Articles
Comments (0)
No comments yet. Be the first!