[FIXED] useEffect Dependencies Explained — No More Infinite Loops or Confusion
⚡ 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 whencountchanges.- 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:
- If your effect doesn’t touch the outside world, you probably don’t need it.
React re-renders handle most logic automatically. - Wrap functions in
useCallbackoruseMemoif they cause unnecessary re-renders. - Understand cleanup.
Cleanup functions stop timers, unsub events, or cancel network calls.
React runs cleanup before the next effect or on unmount. - 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.” 🙃
![[FIXED] useEffect Dependencies Explained — No More Infinite Loops or Confusion](/_next/image?url=https%3A%2F%2Fapi.techgeeta.com%2Fstorage%2Fimages%2F1761901984.png&w=1920&q=75)