🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

React useEffect Infinite Loop: Why It Happens and Exactly How to Fix It

⏱️8 min read  ·  1,717 words

{
“@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.

🔑 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

  1. Add console.log before the useEffect: See how often the component re-renders.
  2. Comment out the state update inside the effect: If the loop stops, the state update is the cause.
  3. Log each dependency: console.log('dep1:', dep1, 'dep2:', dep2) inside the effect to see which one changed.
  4. Use the ESLint react-hooks plugin: eslint-plugin-react-hooks flags 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.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা