Almost everyone who studies React eventually runs into useEffect, and many people start using it too early and too often. It is common to think, “something changed, so I probably need an effect,” and that is how loops, awkward dependencies, and confusing control flow begin.
useEffect is powerful, but it is also one of the easiest ways to make React code harder to understand. That is why it helps to learn the purpose before the syntax.
This post covers three things.
- when
useEffectis actually the right tool - how to think about dependency arrays
- which problems are simpler without an effect at all
The key idea is this: useEffect is for synchronizing with the outside world after rendering.
What React useEffect is for
useEffect lets you run logic after React renders. It is commonly used for cases like these.
- fetching data from an API
- adding and removing event listeners
- starting and cleaning up timers
- syncing with browser APIs
- coordinating React state with external libraries
useEffect(() => {
async function fetchPosts() {
const response = await fetch('/api/posts');
const data = await response.json();
setPosts(data);
}
fetchPosts();
}, []);
An empty dependency array means the effect is designed to run on mount.
When you really need useEffect
A simple test is to ask whether rendering alone can solve the problem. If the answer is no because the logic reaches outside React, an effect is often appropriate.
Good examples include:
- subscribing to browser events
- starting intervals or timeouts that need cleanup
- sending network requests after a component appears
- syncing document title or another browser-managed value
useEffect(() => {
function handleScroll() {
console.log(window.scrollY);
}
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
This is a classic effect because registration and cleanup both matter.
When you should avoid useEffect
Many things that beginners place inside effects are really just calculations or event logic.
You often do not need an effect for:
- derived values from existing props or state
- click-based logic that can run directly in a handler
- simple form updates
- values that can be calculated during render
For example, this does not need an effect.
const totalPrice = items.reduce((sum, item) => sum + item.price, 0);
If you create separate state like totalPrice and keep it in sync through useEffect, you usually add complexity without adding value.
How to think about dependency arrays
The dependency array declares which values an effect should react to. If the effect uses values that can change between renders, those values usually belong in the array.
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
Here, the browser title should update whenever count changes, so [count] makes sense.
A common trap is forcing the array to stay empty to silence warnings. If a warning appears, it is usually better to ask whether the effect belongs somewhere else or whether the logic should be written differently.
Common mistakes
1. Creating derived state inside an effect
Values like filteredItems, fullName, or isFormValid are often simpler as direct calculations.
2. Moving event-driven logic into effects
If a button click should trigger work, a click handler is usually clearer than an effect that reacts later to changed state.
3. Forgetting cleanup
Intervals, subscriptions, and event listeners need cleanup. Otherwise duplicated behavior and memory leaks become more likely.
4. Repeating fetch boilerplate everywhere
On small demos, direct fetch logic inside useEffect is fine. In bigger apps, loading state, error state, retries, and caching often repeat enough that a custom hook becomes worthwhile.
Questions to ask before writing an effect
- Am I synchronizing with something outside React?
- Could this be a direct calculation during render?
- Could this run in an event handler instead?
- Does this need cleanup?
- Is this pattern repeating enough to extract into a hook?
Just asking these questions removes a lot of unnecessary effects.
FAQ
Q. Does all data fetching have to happen in useEffect?
Not always. In many setups it is common, but frameworks and data libraries may offer better patterns.
Q. Should I ignore dependency warnings if the code seems to work?
Usually no. Those warnings often point to unstable behavior that may fail later.
Q. Is having many effects automatically a code smell?
Not automatically, but more effects usually mean more synchronization points, which is worth reviewing.
Read Next
- If you want to organize repeated effect logic, continue with React Custom Hooks Guide.
- If you want a better sense of when rendering work is actually expensive, React Rendering Optimization Guide is a strong follow-up.
While AdSense review is pending, related guides are shown instead of ads.
Start Here
Continue with the core guides that pull steady search traffic.
- Middleware Troubleshooting Guide: Redis vs RabbitMQ vs Kafka A practical middleware troubleshooting guide for developers covering when to reach for Redis, RabbitMQ, or Kafka symptoms first, and which problem patterns usually belong to each tool.
- Kubernetes CrashLoopBackOff: What to Check First A practical Kubernetes CrashLoopBackOff troubleshooting guide covering startup failures, probe issues, config mistakes, and what to inspect first.
- Kafka Consumer Lag Increasing: Troubleshooting Guide A practical Kafka consumer lag troubleshooting guide covering what lag usually means, which consumer metrics to check first, and how poll timing, processing speed, and fetch patterns affect lag.
- Kafka Rebalancing Too Often: Common Causes and Fixes A practical Kafka troubleshooting guide covering why consumer groups rebalance too often, what poll timing and group protocol settings matter, and how to stop rebalances from interrupting useful work.
- Docker Container Keeps Restarting: What to Check First A practical Docker restart-loop troubleshooting guide covering exit codes, command failures, environment mistakes, health checks, and what to inspect first.
While AdSense review is pending, related guides are shown instead of ads.