{
“@context”: “https://schema.org”,
“@type”: “TechArticle”,
“headline”: “React useEffect Infinite Loop: Why It Happens and Exactly How to Fix It”,
“description”: “Complete guide to diagnosing and fixing React useEffect infinite loops โ dependency array mistakes, object/function deps, and all common patterns.”,
“url”: “https://techpulsesite.com/react-useeffect-infinite-loop-why-it-happens-and-exactly-how-to-fix-it/”,
“datePublished”: “2026-06-25T16:05:00+00:00”,
“dateModified”: “2026-06-29T04:14:42+00:00”,
“author”: {
“@type”: “Organization”,
“name”: “TechPulse Editorial Team”,
“url”: “https://techpulsesite.com”
},
“publisher”: {
“@type”: “Organization”,
“name”: “TechPulse”,
“url”: “https://techpulsesite.com”
},
“inLanguage”: “en”
}
{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “When should I use an empty dependency array?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “When the effect should only run once on mount (e.g., fetch initial data, set up a subscription). Be careful โ it creates a closure over the initial state values.”
}
},
{
“@type”: “Question”,
“name”: “Should I always include everything in the dependency array?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Yes, per React’s rules. If including something causes a loop, the real fix is to stabilize that value with useMemo/useCallback, not to remove it from deps.”
}
},
{
“@type”: “Question”,
“name”: “What’s the difference between useMemo and useCallback?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “useMemo memoizes a value (e.g., a filtered array). useCallback memoizes a function. Both prevent unnecessary re-creation on every render.”
}
},
{
“@type”: “Question”,
“name”: “Why does React run useEffect twice in development?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “React 18 Strict Mode runs effects twice intentionally in development to help find side effects that don’t clean up properly. This doesn’t happen in production.”
}
},
{
“@type”: “Question”,
“name”: “Can I disable ESLint’s exhaustive-deps rule?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “You can, but you shouldn’t. The rule exists because missing dependencies cause subtle bugs. Fix the underlying problem instead of disabling the warning.”
}
}
]
}
{
“@context”: “https://schema.org”,
“@type”: “TechArticle”,
“headline”: “React useEffect Infinite Loop: Why It Happens and Exactly How to Fix It”,
“description”: “Complete guide to diagnosing and fixing React useEffect infinite loops โ dependency array mistakes, object/function deps, and all common patterns.”,
“url”: “https://techpulsesite.com/react-useeffect-infinite-loop-why-it-happens-and-exactly-how-to-fix-it/”,
“datePublished”: “2026-06-25T16:05:00+00:00”,
“dateModified”: “2026-06-29T02:28:39+00:00”,
“author”: {
“@type”: “Organization”,
“name”: “TechPulse Editorial Team”,
“url”: “https://techpulsesite.com”
},
“publisher”: {
“@type”: “Organization”,
“name”: “TechPulse”,
“url”: “https://techpulsesite.com”
},
“inLanguage”: “en”
}
{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “When should I use an empty dependency array?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “When the effect should only run once on mount (e.g., fetch initial data, set up a subscription). Be careful โ it creates a closure over the initial state values.”
}
},
{
“@type”: “Question”,
“name”: “Should I always include everything in the dependency array?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Yes, per React’s rules. If including something causes a loop, the real fix is to stabilize that value with useMemo/useCallback, not to remove it from deps.”
}
},
{
“@type”: “Question”,
“name”: “What’s the difference between useMemo and useCallback?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “useMemo memoizes a value (e.g., a filtered array). useCallback memoizes a function. Both prevent unnecessary re-creation on every render.”
}
},
{
“@type”: “Question”,
“name”: “Why does React run useEffect twice in development?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “React 18 Strict Mode runs effects twice intentionally in development to help find side effects that don’t clean up properly. This doesn’t happen in production.”
}
},
{
“@type”: “Question”,
“name”: “Can I disable ESLint’s exhaustive-deps rule?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “You can, but you shouldn’t. The rule exists because missing dependencies cause subtle bugs. Fix the underlying problem instead of disabling the warning.”
}
}
]
}
{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “When should I use an empty dependency array?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “When the effect should only run once on mount (e.g., fetch initial data, set up a subscription). Be careful โ it creates a closure over the initial state values.”
}
},
{
“@type”: “Question”,
“name”: “Should I always include everything in the dependency array?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Yes, per React’s rules. If including something causes a loop, the real fix is to stabilize that value with useMemo/useCallback, not to remove it from deps.”
}
},
{
“@type”: “Question”,
“name”: “What’s the difference between useMemo and useCallback?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “useMemo memoizes a value (e.g., a filtered array). useCallback memoizes a function. Both prevent unnecessary re-creation on every render.”
}
},
{
“@type”: “Question”,
“name”: “Why does React run useEffect twice in development?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “React 18 Strict Mode runs effects twice intentionally in development to help find side effects that don’t clean up properly. This doesn’t happen in production.”
}
},
{
“@type”: “Question”,
“name”: “Can I disable ESLint’s exhaustive-deps rule?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “You can, but you shouldn’t. The rule exists because missing dependencies cause subtle bugs. Fix the underlying problem instead of disabling the warning.”
}
}
]
}
{
“@context”: “https://schema.org”,
“@type”: “TechArticle”,
“headline”: “React useEffect Infinite Loop: Why It Happens and Exactly How to Fix It”,
“description”: “Complete guide to diagnosing and fixing React useEffect infinite loops โ dependency array mistakes, object/function deps, and all common patterns.”,
“url”: “”,
“datePublished”: “2026-06-25 16:05:00”,
“dateModified”: “2026-06-25 16:05:00”,
“author”: {
“@type”: “Organization”,
“name”: “TechPulse Editorial Team”,
“url”: “https://techpulsesite.com”
},
“publisher”: {
“@type”: “Organization”,
“name”: “TechPulse”,
“url”: “https://techpulsesite.com”,
“logo”: {
“@type”: “ImageObject”,
“url”: “https://techpulsesite.com/wp-content/uploads/logo.png”
}
}
}
A React useEffect infinite loop is one of the most common bugs for developers learning hooks. Your component re-renders, triggers the effect, which causes another render, which triggers the effect again โ until the browser crashes. Here’s why it happens and exactly how to fix every variant.
๐ Table of Contents
- Why Infinite Loops Happen
- Cause 1: Setting State Without Dependencies (Most Common)
- Cause 2: Object in Dependency Array
- Cause 3: Function in Dependency Array
- Cause 4: State Update with State as Dependency
- Cause 5: Array in Dependency Array
- How to Debug Any Infinite Loop
- Quick Reference: When to Use Each Hook
- Frequently Asked Questions
- Conclusion
๐ Key Takeaway
A React useEffect infinite loop is one of the most common bugs for developers learning hooks. Your component re-renders, triggers the effect, which causes another render, which triggers the effect again โ until the browser crashes.
Why Infinite Loops Happen
Every re-render of a React component creates new references for objects, arrays, and functions defined inside the component. If your useEffect dependency array contains one of these values, React sees a “new” dependency on every render โ even if the data is logically identical โ and runs the effect again, causing another re-render, creating another loop.
Cause 1: Setting State Without Dependencies (Most Common)
// ๐ Bug: runs after every render, sets state, causes re-render
useEffect(() => {
setCount(count + 1); // state update โ re-render โ effect runs again
}); // no dependency array = runs after EVERY render
// โ
Fix: empty dependency array = run once on mount only
useEffect(() => {
setCount(prevCount => prevCount + 1);
}, []); // runs once
Cause 2: Object in Dependency Array
// ๐ Bug: new object reference on every render
function UserProfile({ userId }) {
const options = { method: 'GET', headers: { 'Auth': 'token' } }; // NEW object each render
useEffect(() => {
fetchUser(userId, options);
}, [options]); // options is always "new" โ infinite loop
}
// โ
Fix 1: move object outside component (if it's static)
const OPTIONS = { method: 'GET', headers: { 'Auth': 'token' } }; // defined once
function UserProfile({ userId }) {
useEffect(() => {
fetchUser(userId, OPTIONS);
}, [userId]); // only depends on userId
}
// โ
Fix 2: useMemo if the object depends on props
function UserProfile({ userId, token }) {
const options = useMemo(
() => ({ method: 'GET', headers: { 'Auth': token } }),
[token] // only recreates when token changes
);
useEffect(() => {
fetchUser(userId, options);
}, [userId, options]);
}
Cause 3: Function in Dependency Array
// ๐ Bug: new function reference on every render
function DataFetcher({ id }) {
const fetchData = async () => { // NEW function each render
const data = await api.get(id);
setData(data);
};
useEffect(() => {
fetchData();
}, [fetchData]); // fetchData is always "new" โ infinite loop
}
// โ
Fix: useCallback to memoize the function
function DataFetcher({ id }) {
const fetchData = useCallback(async () => {
const data = await api.get(id);
setData(data);
}, [id]); // only recreates when id changes
useEffect(() => {
fetchData();
}, [fetchData]); // stable reference now
}
// โ
Alternative: define function INSIDE the effect
useEffect(() => {
const fetchData = async () => {
const data = await api.get(id);
setData(data);
};
fetchData();
}, [id]); // clean, no useCallback needed
Cause 4: State Update with State as Dependency
// ๐ Bug: count is dependency AND being updated inside effect
useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1); // depends on count โ loop
}, 1000);
return () => clearInterval(timer);
}, [count]); // re-runs every time count changes
// โ
Fix: use functional state update (no dependency on count)
useEffect(() => {
const timer = setInterval(() => {
setCount(prev => prev + 1); // no dependency on count needed
}, 1000);
return () => clearInterval(timer);
}, []); // runs once, updates count independently
Cause 5: Array in Dependency Array
// ๐ Bug: new array reference on every render
function List({ items }) {
const filtered = items.filter(i => i.active); // NEW array each render
useEffect(() => {
processItems(filtered);
}, [filtered]); // always "new" โ infinite loop
}
// โ
Fix: useMemo to stabilize the array
function List({ items }) {
const filtered = useMemo(
() => items.filter(i => i.active),
[items]
);
useEffect(() => {
processItems(filtered);
}, [filtered]);
}
How to Debug Any Infinite Loop
- Add console.log before the useEffect: See how often the component re-renders.
- Comment out the state update inside the effect: If the loop stops, the state update is the cause.
- Log each dependency:
console.log('dep1:', dep1, 'dep2:', dep2)inside the effect to see which one changed. - Use the ESLint react-hooks plugin:
eslint-plugin-react-hooksflags missing dependencies automatically.
Quick Reference: When to Use Each Hook
| Hook | Use For |
|---|---|
| useEffect | Side effects: data fetching, subscriptions, DOM manipulation |
| useMemo | Expensive computed values โ prevents recalculation on every render |
| useCallback | Functions passed as dependencies or props to memoized children |
| useRef | Values that shouldn’t trigger re-renders when changed |
Frequently Asked Questions
Q: When should I use an empty dependency array?
A: When the effect should only run once on mount (e.g., fetch initial data, set up a subscription). Be careful โ it creates a closure over the initial state values.
Q: Should I always include everything in the dependency array?
A: Yes, per React’s rules. If including something causes a loop, the real fix is to stabilize that value with useMemo/useCallback, not to remove it from deps.
Q: What’s the difference between useMemo and useCallback?
A: useMemo memoizes a value (e.g., a filtered array). useCallback memoizes a function. Both prevent unnecessary re-creation on every render.
Q: Why does React run useEffect twice in development?
A: React 18 Strict Mode runs effects twice intentionally in development to help find side effects that don’t clean up properly. This doesn’t happen in production.
Q: Can I disable ESLint’s exhaustive-deps rule?
A: You can, but you shouldn’t. The rule exists because missing dependencies cause subtle bugs. Fix the underlying problem instead of disabling the warning.
Conclusion
React useEffect infinite loops always come down to one root cause: a dependency that creates a new reference on every render, causing the effect to re-run, which triggers another render. The fix is always to stabilize that reference using useMemo, useCallback, useRef, or by moving the value outside the component. Once you internalize the “same reference = no re-run” mental model, these bugs become easy to prevent and fast to debug.
๐ You might also like
๐ Share this article




โ๏ธ Leave a Comment