[FIXED] useEffect Dependencies Explained — No More Infinite Loops or Confusion

By TechGeeta
[FIXED] useEffect Dependencies Explained — No More Infinite Loops or Confusion
4 min read

⚡ TL;DR:

useEffect runs to synchronize your React component with the “outside world.” The dependency array tells React when to re-run it. Forget something inside the dependency list, and your app acts weird. Add too much, and it keeps re-running forever. The trick? Understand what actually changes — and React will handle the rest.


🎢 Why useEffect Dependencies Feel Like a Trap (Until They Don’t)

If you’ve ever used React’s useEffect and felt like you accidentally summoned a glitchy infinite loop demon — congratulations, you’re officially a React developer. 🎉

Let’s be honest — useEffect is that one hook that makes you question everything you thought you knew about re-renders, closures, and the meaning of life.


🤯 So... what does useEffect actually do?

According to the official docs (and that one senior dev on your team who types faster than you breathe):

“useEffect lets you synchronize a component with an external system.”

Translation?
Whenever your component needs to do something outside React’s cozy world — like fetch data, subscribe to an event, or start a timer — useEffect is your bridge.

Example:

import { useEffect } from "react";

function Timer() {
  useEffect(() => {
    const id = setInterval(() => {
      console.log("Tick...");
    }, 1000);

    // cleanup
    return () => clearInterval(id);
  }, []);

  return <p>⏰ Time is ticking...</p>;
}

The above code sets up a timer when your component mounts — and clears it when the component unmounts.
Simple, right?
Until you start adding stuff inside that dependency array.


🕳️ The Dependency Black Hole

Here’s the real confusion: that innocent-looking second argument — [].

useEffect(() => {
  console.log("Effect runs!");
}, [count]);

It looks harmless. But what you put in there determines your app’s fate.

Let’s decode this madness:

  • [] → Run only once (on mount).
  • [count] → Run when count changes.
  • No array at all? → Run after every render. Every. Single. Time.

😅 “Okay, but why does my effect keep running forever?”

Ah yes, the classic infinite loop — the React rite of passage.

Consider this gem:

useEffect(() => {
  setUserData({ name: "Sourav" });
}, [userData]);

Every render updates userData, which triggers useEffect again, which updates userData again…
And before you know it, Chrome starts begging for mercy.

Moral of the story:
Don’t put things in the dependency array that change because of the effect itself.
That’s like yelling at your own echo.


🧩 The “Reactive” Values Rule

React docs say — and it’s actually right this time — you should include every reactive value you use inside your effect.

Reactive values = props, state, or anything defined inside your component.

So, if your effect uses roomId or serverUrl, they go in the array:

useEffect(() => {
  const connection = createConnection(serverUrl, roomId);
  connection.connect();
  return () => connection.disconnect();
}, [serverUrl, roomId]);

Now, if either of these changes, React re-runs your effect — cleanly disconnecting the old connection first.
No leaks, no drama.


🧠 The Senior Dev Trap: Over-Optimization

You know what’s worse than missing a dependency? Removing one because “it still works.”

React’s dependency rules exist for a reason — not (only) to torture you.
If your linter complains, it’s probably right.
If you silence it with // eslint-disable-next-line react-hooks/exhaustive-deps, it’s probably watching you. 👀


⚔️ How to Actually Stay Sane

Here are some quick survival tips:

  1. If your effect doesn’t touch the outside world, you probably don’t need it.
    React re-renders handle most logic automatically.
  2. Wrap functions in useCallback or useMemo if they cause unnecessary re-renders.
  3. Understand cleanup.
    Cleanup functions stop timers, unsub events, or cancel network calls.
    React runs cleanup before the next effect or on unmount.
  4. Remember: Effects run twice in dev (Strict Mode).
    It’s not a bug. It’s React making sure you didn’t break something.
    (Yeah, we all thought it was a bug the first time.)

🪄 The Real Secret: Think “Setup + Cleanup,” Not “Mount + Update”

When writing useEffect, stop thinking about component lifecycle.
Instead, think of it as a self-contained mini-process:

  • “What should start when this runs?”
  • “What should stop when it’s replaced or destroyed?”

That’s it. React handles the rest.


⚡ Myth-Buster: “React fixed the infinite useEffect loop”

Heard this one? Yeah — rumor alert. React didn’t “fix” the infinite re-render loops; it just got smarter about warning you when your dependencies are wrong.
The truth is: if your effect updates a state that’s also in its dependency list — congratulations, you just built a self-sustaining time loop. 😅

React still does exactly what it’s supposed to:

It re-runs your effect whenever any dependency changes.

So if that dependency changes every render (like a new function or object reference), React politely — and endlessly — re-runs it.
The fix? Memoize functions (useCallback), avoid inline objects, and never blame React for doing what you told it to.


😂 Final Thoughts

useEffect isn’t evil — it’s just misunderstood.
The dependency array isn’t your enemy — it’s your honest coworker reminding you what you actually depend on.

So next time your effect runs twice, infinitely, or not at all…
take a breath, check your dependencies, and whisper softly:

“I know what you’re doing, React. And I respect it.” 🙃

About the Author

TechGeeta Solutions

TechGeeta Solutions

Service cum Product Based Startup

India 🇮🇳

TechGeeta Solutions builds scalable, high-performance web and native mobile applications. Focused on clean architecture, rapid execution, and user-centric design, it delivers reliable digital products for modern businesses.

Stay Updated with Our Latest News

Subscribe to our newsletter and be the first to know about our latest projects, blog posts, and industry insights.