Stop using useEffect? Not exactly – but you should use it smarter.
People love bold headlines. But React hasn’t abandoned useEffect; React’s ecosystem has now become smarter so that you don’t abuse it. That means some of the old patterns people wrote 3+ years ago aren’t necessary, and the new tools offer better options for many cases that were once dominated by useEffect.
The Evolution of React Hooks (2018 → 2026)
To understand why useEffect is no longer the centerpiece of modern React, you have to understand how we got here. This transformation didn’t happen overnight. It’s the result of eight years of React evolving from a simple UI library into a full-stack rendering system.
2018: Hooks replace class components
Before 2018, React components were divided between class components (stateless) and functional components (stateless). Lifecycle methods like componentDidMount, componentDidUpdate, and componentWillUnmount handled the side effects. The problem? Class logic was fragmented, difficult to reuse, and painful to reason about at scale.
Hooks arrived in React 16.8 and changed everything. useState gave the functions state. useEffect became a universal lifecycle replacement. For the first time, developers could write completely stateful logic without classes.
And that’s where the overuse started.
useEffect became a Swiss Army Knife. Data fetching? useEffect. Syncing props with state? useEffect. Animation? useEffect. Analytics? useEffect. It worked — but it promoted patterns that were fragile, verbose, and often wrong.
2019–2021: The Dependency Array Era
As hooks adoption exploded, so did bugs.
Developers were struggling with dependency arrays. Leave something out, and you get a stale closure. Put too much in, and you trigger infinite loops. Race conditions became common in data fetching. Memory leaks from forgotten cleanups were everywhere.
The React team kept repeating the same message:
“If you’re using
useEffectto get state from state, you’re doing it wrong.”
But the ecosystem has yet to offer a better primitive. So the industry continued to stack patterns on top of useEffect: custom hooks, lint rules, fetch libraries, caching layers. The codebases worked — but they weren’t elegant.
2022–2023: Concurrent React and Suspense go mainstream
React 18 introduced concurrent rendering and static suspense for data fetching. This was the first sign that React wanted to move data orchestration out of effects and into the render pipeline.
Suspense allowed components to “wait” for data during render instead of after render. That single change undermined the historical reason for the use-in-useeffect pattern.
But the real change was still coming.
2023–2025: The advent of React Server Components
React Server Components (RSC) completely flipped the model. Components can now run on the server, fetch data directly, and stream HTML to the client – without sending fetching code to the browser.
If a component renders on the server, there is no browser lifecycle. There is no mount. No effects. Which means that useEffect no longer exists in large parts of modern React applications.
Frameworks like Next.js App Router made this the default mental model: fetch on the server, render once, hydrate minimal client code.
2025–2026: use() hook and action API
React 19 introduced a static use() hook for opening promises within render and new Action APIs for handling mutations and forms. This eliminated the last major reason developers reached for useEffect: manual async orchestration and loading state management.
Now data, transitions, and mutations reside in dedicated primitives – not in scattered side-effect handlers.
Result: A New Mental Model
By 2026, React is no longer just a “UI library“. It is a full-stack rendering engine where:
- Data fetching occurs before rendering
- Side effects are rare and intentional
- Server and client responsibilities are clear
- Performance is automatic, not manually memoized
useEffect is not dead. It simply returned to its original purpose: to handle true external side effects – not to be the basis of every component.
And this is how a mature structure should develop.
Let’s get down to the facts – no hype.
What is useEffect actually?
React’s useEffect is a built-in hook that runs code after a component is rendered and optionally cleans itself up afterwards. That’s literally its job: post-render side effects that can’t happen at render time.
Examples include:
- Subscribing/unsubscribing to external event sources
- Setting timers
- Integrating with non-React APIs (such as Canvas Animation, D3, WebSockets)
- Directly manipulating DOM elements when needed
- Analytics, logging, performance metrics
- Client-only side effects that can’t run on the server
React also recognizes that if you’re not connecting to something external, you probably shouldn’t be using it.
So here’s the brutal truth:
useEffectwas never meant to be a general purpose “lifecycle tool”… people just abused it. That’s on you, not React.
Getting angry at useEffect because you’ve used it for everything is like getting angry at a hammer for denting a car.
What has really changed in React (React 19 Era)
Yes – React has come a long way, especially since React 19:
1) use() hook for data fetching
React 19 introduced use() – officially, not experimentally – to handle async promises directly inside components. It removes a lot of the typical useEffect boilerplate: loading states, setting state inside the effect, manual error catching, etc.
Now you can:
const user = use(fetchUser());
No useEffect, no useState dance.
It’s a win – but with a catch: it’s intended for React Server Components or Suspense boundaries, not arbitrary async everywhere.
React’s own documentation makes this clear: useEffect only run on the client, while server components don’t run effects at all.
So use() is a powerful replacement for fetching data in the right context – not a 1:1 replacement for everything.
2) Server Components Eliminate Client Side Fetching
React Server Components (RSC) are now mainstream with frameworks like Next.js 14+. This runs entirely on the server and doesn’t even have hydrating JavaScript by default. Because of that:
- You can fetch data directly with
awaitinside the component - You don’t use
useEffectat all in server components - Data comes before render
Boom: No need for client fetch effects.
It’s a big change – but that doesn’t mean useEffect is gone. It just means that one of its biggest use cases from the past – data fetching – has a better home.

3) Action State API (like useActionState)
React now has hooks like useActionState to manage state associated with form submissions or server actions. These hooks handle pending/result states internally instead of manually setting local state within the effect.
You can still use effects with forms, but this API makes the pattern less noisy.
Bottom line: New React features remove boilerplate, not the concept of effects.
Here is the real pattern evolution (not the hype)
Old (Pre-React 18 Era)
useEffect(() => {
fetchData().then(setData);
}, [id]);
Lots of boilerplate and bugs (missing dap, infinite loops).
React 19+ Better Patterns
Get before rendering (server component or suspense):
const data = use(fetchData(id));
Clean, no local state management.
When you should still use useEffect (True)
People selling “useEffect is dead” are either inexperienced or are clickbaiting.
Here are real situations where useEffect is still appropriate:
1. DOM-only browser API
Things like:
- Focusing on an element
- Reading dimensions (getBoundingClientRect)
- Window resize / scroll listeners
These cannot run in SSR and must be run after Mount → Effects.
2. Integrating Imperative Libraries
If you are connecting:
- D3
- Leaflet/Google Maps
- Canvas or WebGL manually
- Third-party UI widgets
These are not declarative and require lifecycle hooks.
3. Subscriptions / Observables
WebSockets, EventSource, Custom Subscriptions – You still need to add setup/cleanup.
4. Analytics, Performance, Logging
This runs after the render and is not just data fetching.
5. Conditional Behavior
Sometimes you need to react to a change in situation rather than getting something.
All of this requires using useEffect (or useLayoutEffect) – and it still does as of early 2025/2026.
That’s not an opinion – this is how React is architected.
What is useEffect no longer needed
These are patterns that people have historically misused useEffect for, but shouldn’t:
1. Data fetching in most cases
Use server components / use() instead.
2. Derivative state synchronization
useEffect(() => {
setOtherState(computeFrom(state));
}, [state]);
The obtained values are included in the render or usememo.
3. Manual “loading” flags
Modern data APIs like use() + suspense provide implicit loading conditions; actions provide pending flags.
4. Manual cleanup of transitions
React’s concurrency and transitions reduce a lot of the awkward effect cleanup logic.
Migration Strategy (No BS)
If you are upgrading an existing project:
Step 1: Audit all use-effect usages
Divide them into these categories:
| Category | Action |
|---|---|
| Data fetching | Replace with use() or Server Component fetch |
| Derived state | Move to render / useMemo |
| DOM interaction | Keep in effect |
| Subscriptions | Keep in effect |
| External libraries | Keep in effect |
That’s it. If there really is side effect logic then don’t rewrite everything, just zip zap “No More Effects”.
Step 2: Convert Fetch Logic
Before:
useEffect(() => {
fetch(`/api/data`).then(r => r.json()).then(setData);
}, [id]);
After (Server Component):
export default async function Page({id}) {
const data = await fetchData(id);
return <ClientView data={data} />;
}
If you need to get clients but want suspense:
const data = use(fetchData(id));
Step 3: Replace Manual Loading Flags
If you have:
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
setIsLoading(true);
fetchData().finally(() => setIsLoading(false));
}, []);
Use Suspense fallback or useActionState for actions.
Step 4: Clean Up Derived State
Stop doing:
useEffect(() => {
setExtra(compute(a, b));
}, [a, b]);
Instead:
const extra = useMemo(() => compute(a, b), [a, b]);
Summary
Here’s the honest score, not a sales pitch:
useEffectis not deprecateduseEffectis still important for true side effects- React 19+ adds powerful new APIs that eliminate many of the abuses of useEffect
- Server components and
use()means that getting data often doesn’t require effects - People should stop using useEffect for things it wasn’t designed for
So the clickbait “useEffect is over” is a half-truth: its dominance is over, not its existence.
Frequently Asked Questions
Q: Is useEffect deprecated?
A: No. It is still a major hook for side effects that can run on the client.
Q: Should I replace all useEffect with use()?
A: No. use() is for async data integration in server components or Suspense. It does not change the effects that interact with external systems.
Q: Are server components widely used?
A: Yes, in frameworks like Next.js 14. They are mainstream for doing logic server-side rendering and fetching.
Q: When can I not use use()?
A: You cannot use it to directly integrate with browser APIs or required third-party libs. It is still an effect domain.
Q: Do effects cause performance issues?
A: It can if misused – e.g., missing dependencies, redundant calls – but that’s a code quality issue, not a reason to scrap them.
Q: What changed the loading state management?
A: The suspense and built-in pending flags reduce boilerplate from things like useActionState.
Q: Does React Compiler (Forget) handle caching automatically?
A: There is a movement towards smart compile-time analysis, but the React team hasn’t shipped a magic compiler that eliminates all performance tuning. Use Memo / derived state patterns where appropriate.
Final Take
The evolution of React isn’t about throwing away core tools – it’s about using the right tools for the job:
- Effects: true side effects
- Async data: Suspense / use() / Server Components
- Form state:
useActionStateor transitions - Derived logic: render / memoization
- Performance: Compiler hints + React concurrency
If you’re still writing everything in Effects because you never learned the modern replacements, that’s your real technical debt – not hooks.
