As a Senior Software Engineer, I’ve seen countless pull requests where useEffect is either doing too much or doing it wrong. And I get it—useEffect is deceptively simple. At first glance, it looks like a catch-all for side effects in React, but if you treat it like a hammer, everything starts to look like a nail.
In this post, I want to walk through what I’ve learned about using useEffect correctly, with real-world examples and mental models that help keep code predictable, clean, and performant.
💡 First: What is useEffect for?#
At its core, useEffect is a way to run side effects in function components—anything that affects something outside the scope of the function itself:
- Fetching data
- Subscribing to events
- Updating the DOM manually
- Working with timers or intervals
But here’s the thing: not all logic needs to go inside useEffect.
🚫 Common Misuse: Treating useEffect as componentDidUpdate#
A common anti-pattern I see looks like this:
This looks fine, but there’s a catch: this derived state should live in a computed value, not state + effect. It creates unnecessary renders and complexity.
✅ Better:
✅ Rule of Thumb #1: Keep it pure, keep it simple#
Your useEffect should ideally:
- Do one thing.
- Be focused on effects: subscriptions, async calls, DOM interactions.
- Not calculate derived state—leave that to
useMemoor inline logic.
🔄 Rule of Thumb #2: Dependencies should match what you use#
If you're using a variable inside useEffect, it should be in the dependency array. Period.
Bad:
Good:
If you’re fighting with the dependency array, that’s a sign the logic may belong elsewhere—or needs refactoring.
🧹 Cleanups Matter: Always return a function if you open something#
Cleanups prevent memory leaks and unexpected behaviors, especially in components that mount and unmount frequently.
🧠 Final Thoughts: Don’t use useEffect until you need to#
Whenever I write a new component, I wait until I truly need an effect. Ask:
- Can this be derived from props or state?
- Is this really a side effect?
- Am I just trying to replicate lifecycle behavior from class components?
Less useEffect usually means simpler code.
🚀 Takeaway#
Mastering useEffect means understanding when not to use it as much as when to reach for it. Keep effects focused, predictable, and side-effectful (pun intended). It’ll make your components easier to test, debug, and scale.
Want to see real-world examples or code audits? Check out my portfolio at iamcamilotavera.com or reach out for a code review.