


You Might Not Need useEffect Anymore in React
By:
Ayushi Chauhan
19 Jun 2025
useEffect
became the most frequently used and, at the same time, the most frequently misused hook in React since its release. It was used to do everything: data retrieval, state synchronization, event listening, and even lifecycle.
However, with the development of React, its best practices change. The introduction of React 19, Server Components, Server Actions, and other developments such as the React Compiler (Forget) are leading the ecosystem towards a more declarative, reactive and server-focused paradigm.
We will deconstruct in this blog:
Why
useEffect
can be more troublesome than helpful?What the modern React provides as alternatives that are cleaner?
Practical cases in which
useEffect
can now be eliminatedWhen is
useEffect
necessary?

What Replaces useEffect
in Modern React?
Why Replace It?
useEffect
is called after render and this makes:
Extra re-renders
Improper layouts and bad user experience
Race conditions and problems that are difficult to debug
Compulsory logic that violates the declarative nature of React
Common useEffect
Use Cases and Their Replacements
Fetching Data from an API
Old way (Client Component):
const [posts, setPosts] = useState([]); useEffect(() => { fetch("/api/posts") .then(res => res.json()) .then(setPosts); }, []);
Modern way (Server Component in Next.js):
import { getPosts } from "@/lib/posts"; export default async function PostsPage() { const posts = await getPosts(); return ( <ul> {posts.map(p => <li key={p.id}>{p.title}</li>)} </ul> ); }
Form Submission and API Calls
Old way:
const handleSubmit = async () => { const res = await fetch("/api/contact", { method: "POST", body: JSON.stringify(formData), }); };
Modern way (Server Actions):
// serverActions.ts "use server"; export async function sendContact(formData: FormData) { await saveToDatabase(formData); } <form action={sendContact}> <input name="name" /> <button type="submit">Send</button> </form>
Syncing State to LocalStorag
Old way:
useEffect(() => { localStorage.setItem("theme", theme); }, [theme]);
Modern way:
import { useLocalStorage } from "react-use"; const [theme, setTheme] = useLocalStorage("theme", "light");
Listening to Scroll or Resize Events
Old way:
useEffect(() => { const handler = () => console.log(window.innerWidth); window.addEventListener("resize", handler); return () => window.removeEventListener("resize", handler); }, []);
Modern way:
import { useWindowSize } from "react-use"; const { width, height } = useWindowSize();
Derived State or Value Syncing
Old way:
useEffect(() => { setFullName(`${firstName} ${lastName}`); }, [firstName, lastName]);
Modern way (direct expression):
const fullName = `${firstName} ${lastName}`;
Modern way (Signals):
import { signal, computed } from "@preact/signals-react"; const firstName = signal("Ayushi"); const lastName = signal("Singh"); const fullName = computed(() => `${firstName.value} ${lastName.value}`);
When You Still Need useEffect
Valid Use Case | Reason to Use |
---|---|
Subscribing to external services | e.g., WebSocket, Firebase |
Manually integrating third-party DOM libraries | like Chart.js |
Logging, analytics, performance monitoring | on mount/unmount |
Event listeners not handled by React | e.g., global escape key |
Signs You're Misusing useEffect
Symptom | Possible Fix |
---|---|
You’re fetching data on every render | Move to Server Component |
You’re syncing two states | Use derived state / signal |
You have a lot of | You may be recreating lifecycle patterns |
You’re debugging stale closures or missing deps | Rethink your design |
Conclusion
useEffect
is not deprecated, but it is not the first tool you have anymore. React 19 and Next.js 14 allow you to express how you want data to be fetched declaratively, Server Actions, and reactive primitives that address most of what useEffect
was used to address, in a cleaner, more efficient way.
Write less code. Ship UI faster. Let React handle effects where it makes sense.
Frequently Asked Questions
1. Is useEffect
still necessary in React 19?
A. Yes, but only for specific use cases like subscribing to external services, handling non-React events, or integrating third-party libraries. With React 19 and features like Server Components and Server Actions, many common useEffect
patterns can now be replaced with cleaner, more declarative solutions.
2. What can I use instead of useEffect
in Next.js 14?
A. In Next.js 14, you can replace many useEffect
cases with Server Components, Server Actions, and the new React Compiler (Forget
). These tools handle data fetching, side effects, and local syncing more efficiently without extra renders or complexity.
3. Why is useEffect
considered bad or misused in React?
A. useEffect
runs after every render, which can cause race conditions, performance issues, and unexpected behaviors. It often breaks the declarative nature of React. That’s why modern React encourages alternatives that align better with its core design principles.
useEffect
became the most frequently used and, at the same time, the most frequently misused hook in React since its release. It was used to do everything: data retrieval, state synchronization, event listening, and even lifecycle.
However, with the development of React, its best practices change. The introduction of React 19, Server Components, Server Actions, and other developments such as the React Compiler (Forget) are leading the ecosystem towards a more declarative, reactive and server-focused paradigm.
We will deconstruct in this blog:
Why
useEffect
can be more troublesome than helpful?What the modern React provides as alternatives that are cleaner?
Practical cases in which
useEffect
can now be eliminatedWhen is
useEffect
necessary?

What Replaces useEffect
in Modern React?
Why Replace It?
useEffect
is called after render and this makes:
Extra re-renders
Improper layouts and bad user experience
Race conditions and problems that are difficult to debug
Compulsory logic that violates the declarative nature of React
Common useEffect
Use Cases and Their Replacements
Fetching Data from an API
Old way (Client Component):
const [posts, setPosts] = useState([]); useEffect(() => { fetch("/api/posts") .then(res => res.json()) .then(setPosts); }, []);
Modern way (Server Component in Next.js):
import { getPosts } from "@/lib/posts"; export default async function PostsPage() { const posts = await getPosts(); return ( <ul> {posts.map(p => <li key={p.id}>{p.title}</li>)} </ul> ); }
Form Submission and API Calls
Old way:
const handleSubmit = async () => { const res = await fetch("/api/contact", { method: "POST", body: JSON.stringify(formData), }); };
Modern way (Server Actions):
// serverActions.ts "use server"; export async function sendContact(formData: FormData) { await saveToDatabase(formData); } <form action={sendContact}> <input name="name" /> <button type="submit">Send</button> </form>
Syncing State to LocalStorag
Old way:
useEffect(() => { localStorage.setItem("theme", theme); }, [theme]);
Modern way:
import { useLocalStorage } from "react-use"; const [theme, setTheme] = useLocalStorage("theme", "light");
Listening to Scroll or Resize Events
Old way:
useEffect(() => { const handler = () => console.log(window.innerWidth); window.addEventListener("resize", handler); return () => window.removeEventListener("resize", handler); }, []);
Modern way:
import { useWindowSize } from "react-use"; const { width, height } = useWindowSize();
Derived State or Value Syncing
Old way:
useEffect(() => { setFullName(`${firstName} ${lastName}`); }, [firstName, lastName]);
Modern way (direct expression):
const fullName = `${firstName} ${lastName}`;
Modern way (Signals):
import { signal, computed } from "@preact/signals-react"; const firstName = signal("Ayushi"); const lastName = signal("Singh"); const fullName = computed(() => `${firstName.value} ${lastName.value}`);
When You Still Need useEffect
Valid Use Case | Reason to Use |
---|---|
Subscribing to external services | e.g., WebSocket, Firebase |
Manually integrating third-party DOM libraries | like Chart.js |
Logging, analytics, performance monitoring | on mount/unmount |
Event listeners not handled by React | e.g., global escape key |
Signs You're Misusing useEffect
Symptom | Possible Fix |
---|---|
You’re fetching data on every render | Move to Server Component |
You’re syncing two states | Use derived state / signal |
You have a lot of | You may be recreating lifecycle patterns |
You’re debugging stale closures or missing deps | Rethink your design |
Conclusion
useEffect
is not deprecated, but it is not the first tool you have anymore. React 19 and Next.js 14 allow you to express how you want data to be fetched declaratively, Server Actions, and reactive primitives that address most of what useEffect
was used to address, in a cleaner, more efficient way.
Write less code. Ship UI faster. Let React handle effects where it makes sense.
Frequently Asked Questions
1. Is useEffect
still necessary in React 19?
A. Yes, but only for specific use cases like subscribing to external services, handling non-React events, or integrating third-party libraries. With React 19 and features like Server Components and Server Actions, many common useEffect
patterns can now be replaced with cleaner, more declarative solutions.
2. What can I use instead of useEffect
in Next.js 14?
A. In Next.js 14, you can replace many useEffect
cases with Server Components, Server Actions, and the new React Compiler (Forget
). These tools handle data fetching, side effects, and local syncing more efficiently without extra renders or complexity.
3. Why is useEffect
considered bad or misused in React?
A. useEffect
runs after every render, which can cause race conditions, performance issues, and unexpected behaviors. It often breaks the declarative nature of React. That’s why modern React encourages alternatives that align better with its core design principles.
useEffect
became the most frequently used and, at the same time, the most frequently misused hook in React since its release. It was used to do everything: data retrieval, state synchronization, event listening, and even lifecycle.
However, with the development of React, its best practices change. The introduction of React 19, Server Components, Server Actions, and other developments such as the React Compiler (Forget) are leading the ecosystem towards a more declarative, reactive and server-focused paradigm.
We will deconstruct in this blog:
Why
useEffect
can be more troublesome than helpful?What the modern React provides as alternatives that are cleaner?
Practical cases in which
useEffect
can now be eliminatedWhen is
useEffect
necessary?

What Replaces useEffect
in Modern React?
Why Replace It?
useEffect
is called after render and this makes:
Extra re-renders
Improper layouts and bad user experience
Race conditions and problems that are difficult to debug
Compulsory logic that violates the declarative nature of React
Common useEffect
Use Cases and Their Replacements
Fetching Data from an API
Old way (Client Component):
const [posts, setPosts] = useState([]); useEffect(() => { fetch("/api/posts") .then(res => res.json()) .then(setPosts); }, []);
Modern way (Server Component in Next.js):
import { getPosts } from "@/lib/posts"; export default async function PostsPage() { const posts = await getPosts(); return ( <ul> {posts.map(p => <li key={p.id}>{p.title}</li>)} </ul> ); }
Form Submission and API Calls
Old way:
const handleSubmit = async () => { const res = await fetch("/api/contact", { method: "POST", body: JSON.stringify(formData), }); };
Modern way (Server Actions):
// serverActions.ts "use server"; export async function sendContact(formData: FormData) { await saveToDatabase(formData); } <form action={sendContact}> <input name="name" /> <button type="submit">Send</button> </form>
Syncing State to LocalStorag
Old way:
useEffect(() => { localStorage.setItem("theme", theme); }, [theme]);
Modern way:
import { useLocalStorage } from "react-use"; const [theme, setTheme] = useLocalStorage("theme", "light");
Listening to Scroll or Resize Events
Old way:
useEffect(() => { const handler = () => console.log(window.innerWidth); window.addEventListener("resize", handler); return () => window.removeEventListener("resize", handler); }, []);
Modern way:
import { useWindowSize } from "react-use"; const { width, height } = useWindowSize();
Derived State or Value Syncing
Old way:
useEffect(() => { setFullName(`${firstName} ${lastName}`); }, [firstName, lastName]);
Modern way (direct expression):
const fullName = `${firstName} ${lastName}`;
Modern way (Signals):
import { signal, computed } from "@preact/signals-react"; const firstName = signal("Ayushi"); const lastName = signal("Singh"); const fullName = computed(() => `${firstName.value} ${lastName.value}`);
When You Still Need useEffect
Valid Use Case | Reason to Use |
---|---|
Subscribing to external services | e.g., WebSocket, Firebase |
Manually integrating third-party DOM libraries | like Chart.js |
Logging, analytics, performance monitoring | on mount/unmount |
Event listeners not handled by React | e.g., global escape key |
Signs You're Misusing useEffect
Symptom | Possible Fix |
---|---|
You’re fetching data on every render | Move to Server Component |
You’re syncing two states | Use derived state / signal |
You have a lot of | You may be recreating lifecycle patterns |
You’re debugging stale closures or missing deps | Rethink your design |
Conclusion
useEffect
is not deprecated, but it is not the first tool you have anymore. React 19 and Next.js 14 allow you to express how you want data to be fetched declaratively, Server Actions, and reactive primitives that address most of what useEffect
was used to address, in a cleaner, more efficient way.
Write less code. Ship UI faster. Let React handle effects where it makes sense.
Frequently Asked Questions
1. Is useEffect
still necessary in React 19?
A. Yes, but only for specific use cases like subscribing to external services, handling non-React events, or integrating third-party libraries. With React 19 and features like Server Components and Server Actions, many common useEffect
patterns can now be replaced with cleaner, more declarative solutions.
2. What can I use instead of useEffect
in Next.js 14?
A. In Next.js 14, you can replace many useEffect
cases with Server Components, Server Actions, and the new React Compiler (Forget
). These tools handle data fetching, side effects, and local syncing more efficiently without extra renders or complexity.
3. Why is useEffect
considered bad or misused in React?
A. useEffect
runs after every render, which can cause race conditions, performance issues, and unexpected behaviors. It often breaks the declarative nature of React. That’s why modern React encourages alternatives that align better with its core design principles.
Explore our services
Explore other blogs
Explore other blogs

let's get in touch
Have a Project idea?
Connect with us for a free consultation !
Confidentiality with NDA
Understanding the core business.
Brainstorm with our leaders
Daily & Weekly Updates
Super competitive pricing

let's get in touch
Have a Project idea?
Connect with us for a free consultation !
Confidentiality with NDA
Understanding the core business.
Brainstorm with our leaders
Daily & Weekly Updates
Super competitive pricing
DEFINITELY POSSIBLE
Our Services
Technologies
Crafted & maintained with ❤️ by our Smartees | Copyright © 2025 - Smartters Softwares PVT. LTD.
Our Services
Technologies
Created with ❤️ by our Smartees
Copyright © 2025 - Smartters Softwares PVT. LTD.