[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.” 🙃

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.