This content originally appeared on Level Up Coding - Medium and was authored by Usama Sarwar

React developers have fallen into a dangerous trap: overusing the useEffect hook.
In countless codebases, we find useEffect sprawling across components like vines, choking readability, performance, and predictability. But what if I told you that most of those useEffect usages aren’t just unnecessary—they're harmful?
Let’s break down this growing problem, understand what the React docs really say, and dive into practical examples of better patterns.
What React Actually Says About useEffect
“Effects are an escape hatch from the React paradigm. They let you ‘step outside’ of React and synchronize your components with some external system like a non-React widget, network, or the browser DOM. If there is no external system involved, you shouldn’t need an Effect.”
This line from the React documentation is often overlooked. It’s clear: useEffect is meant for side effects, not for syncing internal state with props, or for controlling component behavior.
But many developers ignore this guidance. Why?
Because useEffect feels like a Swiss Army knife. It gives you the power to "react" to any change, but just because you can use it doesn't mean you should.
Why Overusing useEffect is an Anti-Pattern
1. It’s Harder to Maintain
Every time you add a useEffect, you're creating a miniature lifecycle. Developers now have to reason not only about component render logic but also about what happens after the render, asynchronously. Over time, these Effects create a tangled web of updates that's incredibly hard to follow.
2. It Slows Down Rendering
useEffect runs after the browser paints the screen. If you’re updating state inside an Effect, you’re triggering another render—after the DOM has already changed. That’s one extra render cycle every time. Multiply this across multiple components and you’ve got noticeable lag.
3. It’s Prone to Bugs
You’ve probably encountered those frustrating cases where a state update inside useEffect causes a flicker, glitch, or even an infinite loop. These bugs are often subtle and hard to trace, especially when Effects depend on state that itself is being updated inside the same Effect.
4. It Spreads Like Wildfire
Once you start using useEffect to update internal state from props, it's difficult to stop. One component needs to react to a prop, another reacts to that component’s state, and before you know it, every file in your codebase has Effects.
5. Mixing Paradigms
React is built on a declarative paradigm where components describe what the UI should look like, not the steps to change it. useEffect introduces imperative logic that runs after rendering, creating a hybrid approach that's hard to reason about.
6. Unclear Execution Order
Effects run after rendering, but the exact timing is unpredictable — especially when multiple effects interact. This temporal indeterminism makes debugging difficult.
If something is designed purely for side effects — as the name useEffect clearly implies — then it should be used that way. Simply updating state inside a useEffect IS NOT A SIDE EFFECT, and using it like that can indicate a misunderstanding of its purpose.
A Common Anti-Pattern: Setting Derived State from Props via useEffect
This is one of the worst and most common anti-patterns in React development:
const Child = ({ prop }) => {
const [value, setValue] = useState(null);
useEffect(() => {
if (prop) {
const updatedValue = someFuncToCalculate(prop);
setValue(updatedValue);
}
}, [prop]);
return <div>{value}</div>;
};
Why this is bad:
- You’re using useEffect to sync a state that is entirely derived from a prop.
- This causes unnecessary indirection, potential bugs, and extra renders.
- If value is just a transformation of prop, it doesn't need to be stored as state at all.
✅ The Right Way: Derive Directly
If the value is calculated solely based on the prop, just compute it directly:
const Child = ({ prop }) => {
const value = someFuncToCalculate(prop);
return <div>{value}</div>;
};
No useState. No useEffect. Clean, efficient, and easier to reason about.
If you’re calculating a value from a prop only, it should not be state — plain and simple.
Resetting Child State from Parent
Another common misuse of useEffect is when developers try to reset child state based on a parent prop:
const Child = ({ shouldReset }) => {
const [field, setField] = useState('');
useEffect(() => {
if (shouldReset) {
setField('');
}
}, [shouldReset]);
return <input value={field} onChange={e => setField(e.target.value)} />;
};
This creates tightly coupled, hard-to-maintain logic. But there’s a cleaner alternative.
✅ Use the key Prop
const Parent = ({ resetCounter }) => {
return <Child key={resetCounter} />;
};
const Child = () => {
const [field, setField] = useState('');
return <input value={field} onChange={e => setField(e.target.value)} />;
};
Every time resetCounter changes, React treats Child as a new component and resets its state. No Effects needed.
Updating State Based on Both Parent Prop and Child Interaction
You might think: “Okay, I have a child state that needs to update based on a prop from the parent, but also from some internal event like a click. I can’t just make this a derived value or plain variable — because the child manages it too. So I have no choice but to use useEffect, right?”
Let’s walk through the current (common) approach:
const Child = ({ prop }) => {
const [value, setValue] = useState(null);
useEffect(() => {
if (prop) {
const updatedValue = someFuncToCalculate(prop);
setValue(updatedValue);
}
}, [prop]);
const clickHandler = () => {
setValue(100);
};
return (
<button onClick={clickHandler}>
Click
</button>
);
};
Now here’s a “better” approach — one that might trigger a few developers:
const Child = ({ prop }) => {
const [value, setValue] = useState(null);
const [prevProp, setPrevProp] = useState(prop);
if (prop !== prevProp) {
setPrevProp(prop);
const updatedValue = someFuncToCalculate(prop);
setValue(updatedValue);
}
const clickHandler = () => {
setValue(100);
};
return (
<button onClick={clickHandler}>
Click
</button>
);
};
Wait, what? Updating state during render?
This seems dangerous. You’re calling setState in the main body of the component, not inside an effect. That can lead to infinite rerenders, right?
Let’s pause.
Why This Isn’t as Bad as It Looks
Yes, this looks risky. But the key point is: the condition is deterministic. It only runs if prop !== prevProp. Which means this branch is controlled—it only runs when the prop changes, which is exactly what the useEffect was doing.
But here’s the critical difference:
- useEffect runs after render and after the DOM has been updated
- This pattern runs during render, before any DOM update
This can matter. For example, the useEffect approach might cause glitches — flickers in the UI — because state is updated only after the DOM updates. Here, we’re updating the state inline as part of React’s render pass. No visual inconsistency. No momentary mismatch.
“But You’re Still Causing an Extra Rerender!”
Yes — and that’s the big insight.
Let’s break it down:
In both cases, when the parent updates the prop, this happens:
1. Parent rerenders
2. Child rerenders
3. Child sees prop change and sets new state
4. Child rerenders again
That’s an extra rerender, no matter whether you’re using useEffect or inline logic.
So why is this “better”? Because:
- It avoids post-render inconsistencies
- It doesn’t rely on lifecycle hooks
- It keeps state logic local and inline with render behavior
You just have to ensure that the update is controlled (like with the prop !== prevProp check) to avoid infinite loops.
Note: This isn’t a universally recommended pattern, but it shows how tightly controlled render-based updates can outperform useEffect in certain cases.
But Still — This Isn’t Ideal Either
Let’s be honest.
Whether you’re using useEffect or this render-time prop tracking pattern, you're in tricky territory. You're syncing a child's local state to a parent prop, and also modifying it internally.
This adds complexity. Debugging becomes harder. Data flow becomes harder to reason about.
The best approach?
Don’t let a child mirror or track a prop in its state at all. Instead:
- Uplift the state to the parent so both can control it
- Or calculate the value on the fly in the child, using the prop
You might think: “But won’t uplifting cause the whole subtree to rerender?”
Yes — but it probably already is. If your parent changes the prop, React is going to rerender the parent and child anyway. Uplifting state often just makes data flow more predictable, not more expensive.
Rule of Thumb
Place state in the right place, not higher or lower than it needs to be.
- Don’t forcefully push state downward (into the child) if it’s ultimately driven by the parent.
- Don’t pull it upward unnecessarily either, especially if only the child cares.
useEffect and Event Handlers: Stop the Indirection
Another terrible pattern is triggering API calls inside useEffect based on state set by an event:
const Component = () => {
const [trigger, setTrigger] = useState(false);
useEffect(() => {
if (trigger) {
fetchData();
}
}, [trigger]);
return <button onClick={() => setTrigger(true)}>Load</button>;
};
This is just… unnecessary indirection.
✅ Better:
const Component = () => {
const handleClick = () => {
fetchData();
};
return <button onClick={handleClick}>Load</button>;
};
The JS event system is asynchronous by design. It doesn’t need to be wrapped in a state + Effect combo to delay execution. Just call the function.
JavaScript’s event system is already asynchronous — click handlers are queued and run via the event loop. Wrapping that logic in a state update and a useEffect only delays the inevitable, adds boilerplate, and complicates the flow. If you’re calling something in direct response to a user event — just call it.
Why deliberately adding latency and indirection like that?
Reactivity vs. Imperative Control
When you write an Effect to update internal state based on external input, you’re mixing React’s declarative model with imperative logic. That’s what causes headaches.
The declarative model says: “The UI reflects the current state.”
The imperative model says: “Do X when Y happens.”
Most useEffect usages are imperative. They create procedural code in a declarative system. No wonder it's confusing.
When SHOULD You Use useEffect?
Let’s be fair — useEffect is useful. But only when you're doing things outside the React rendering cycle:
Valid use cases:
- Subscribing to external services (e.g., WebSocket, Redux store, database listener)
- Fetching data when a component appears on screen
- Updating DOM elements manually (rare but valid)
- Setting up or cleaning timers, intervals, or global event listeners
Rule of Thumb:
If the logic is:
- Caused by a user event → use event handler
- Caused by component mount or unmount → use Effect
- Derived from props or state → use computed values, memoization, or conditional rendering, not Effects.
Best Practices and Final Thoughts
- Avoid state unless necessary. If something can be computed, compute it.
- Avoid useEffect unless you're dealing with side effects. Just because it's available doesn't mean it's the best option.
- Don’t sync props to state. If you must, consider other patterns like controlled components, lifting state, or even resetting components with the key prop.
- Understand re-renders. useEffect causes them after DOM updates, which can cause visual glitches.
- Know your lifecycle. useEffect doesn’t run during render; it runs after paint.
TL;DR
React’s useEffect is for side effects. Not for setting state based on props. Not for handling internal logic. Not for reacting to events. Overusing it is a code smell and a sign of misunderstanding the React paradigm.
Focus on clean, declarative logic. Place your state at the right level. Trust React’s rendering model, and you’ll write components that are more predictable, maintainable, and performant.
Let’s stop writing React like it’s jQuery with hooks.
Let’s clean up our components. Let’s stop abusing useEffect.
Stop Overusing useEffect in React was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Level Up Coding - Medium and was authored by Usama Sarwar

Usama Sarwar | Sciencx (2025-05-19T02:45:59+00:00) Stop Overusing useEffect in React. Retrieved from https://www.scien.cx/2025/05/19/stop-overusing-useeffect-in-react/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.