ReactuseEffectData FetchingBest Practices

Why Fetching Data Inside useEffect Is Not a Good Idea

Discover why fetching data inside React's useEffect can cause headaches, and learn the better ways to handle data in modern React apps.

Pedro Tech
September 4, 2025
6 min read
Why Fetching Data Inside useEffect Is Not a Good Idea

Why Fetching Data Inside useEffect Is Not a Good Idea


Introduction


If you've ever sprinkled a fetch call inside a useEffect and thought "hey, this works," you're not alone. I did the same thing when I first learned React. But after a while, I realized it's like trying to cook pasta in a coffee machine. Sure, it technically works, but it's messy and leaves a weird aftertaste.


Let’s talk about why using useEffect for data fetching is usually not the best move and what you can do instead.


Why It Feels Natural (But Isn't)


When you start with React, the mental model is simple: component mounts, run useEffect, fetch the data, set state, done. It feels clean, but behind the scenes, things get messy.


Problems You’ll Run Into


  • Double fetching in Strict Mode

React’s development Strict Mode runs effects twice on mount. That means your fetch runs twice too, and suddenly you’re staring at duplicate API requests.


  • Race conditions

Imagine navigating between pages quickly. One useEffect kicks off a fetch, then another kicks off right after. If the slower one finishes last, it can overwrite your newer data. That's like getting last week's weather update instead of today’s.


  • No built-in caching

Fetching in useEffect means every mount re-fetches, even if the data hasn’t changed. It’s like asking a waiter for the menu every five minutes.


  • Complicated cleanup

You might add AbortControllers or flags to prevent memory leaks. At that point, you’re spending more time babysitting effects than building features.


What to Do Instead


Option 1: React Query (TanStack Query)


A library like React Query is like hiring a personal assistant for your data. It caches, deduplicates, retries, and even keeps things fresh automatically.


1import { useQuery } from "@tanstack/react-query";
2
3function Todos() {
4  const { data, isLoading, error } = useQuery({
5    queryKey: ["todos"],
6    queryFn: () => fetch("/api/todos").then((res) => res.json()),
7  });
8
9  if (isLoading) return <p>Loading...</p>;
10  if (error) return <p>Something went wrong</p>;
11
12  return (
13    <ul>
14      {data.map((todo) => (
15        <li key={todo.id}>{todo.text}</li>
16      ))}
17    </ul>
18  );
19}

No more worrying about stale data or duplicate requests. React Query has your back.


Option 2: Next.js Server Components


If you’re in a Next.js project, server components are the shiny new toy. Fetch your data on the server, send it down with the HTML, and skip the client-side fetching headache.


1// app/page.tsx
2async function getTodos() {
3  const res = await fetch("https://jsonplaceholder.typicode.com/todos");
4  return res.json();
5}
6
7export default async function Page() {
8  const todos = await getTodos();
9  return (
10    <ul>
11      {todos.slice(0, 5).map((todo) => (
12        <li key={todo.id}>{todo.title}</li>
13      ))}
14    </ul>
15  );
16}

This way your users see content instantly, and search engines love you for it.


A Quick Analogy


Fetching in useEffect is like making toast by holding bread over a candle. It works, but why would you do that when you’ve got a toaster sitting right there?


Conclusion


Yes, you can fetch data in useEffect, but it quickly leads to bugs, wasted requests, and unnecessary complexity. Modern React has way better solutions like React Query or server components in Next.js. Once you switch, you’ll never want to go back.

Ready to Master React?

You've learned the fundamentals from this article. Now take your skills to the next level with our comprehensive React course.

Enroll in Course
Join thousands of developers already learning