DEV Community

Safal Bhandari
Safal Bhandari

Posted on

Replacing Manual Data Fetching With SWR in React

When working with APIs in React, a common pattern is to use useEffect and useState with a library like Axios to fetch data. While this works, it can become verbose, especially when you add polling, caching, or revalidation. This is where SWR comes in.

In this article, we’ll walk through:

  1. A manual approach with useEffect.
  2. A buggy hybrid where SWR and the manual state approach are mixed.
  3. A clean SWR solution that replaces the boilerplate.

1. Manual Hook With useEffect

Here’s a simple custom hook that fetches todos every timeout seconds:

import { useEffect, useState } from "react";
import axios from "axios";

function useTodos(timeout) {
  const [todos, setTodos] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchTodos = () => {
      axios.get("https://dummyjson.com/todos").then((res) => {
        setTodos(res.data.todos);
        setLoading(false);
      });
    };

    // Initial fetch
    fetchTodos();

    // Poll every `timeout` seconds
    const reRunning = setInterval(fetchTodos, timeout * 1000);

    return () => clearInterval(reRunning);
  }, [timeout]);

  return { todos, loading };
}
Enter fullscreen mode Exit fullscreen mode

And the component using it:

function App() {
  const { todos, loading } = useTodos(6);

  if (loading) return <p>Loading...</p>;

  return (
    <>
      {todos.map((todo) => (
        <Track todo={todo} key={todo.id} />
      ))}
    </>
  );
}

function Track({ todo }) {
  return (
    <div className="font-black">
      {todo.id} - {todo.todo}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

This works fine, but you have to manage state, effects, and cleanup yourself.


2. Mixing useEffect and SWR (What Went Wrong)

When switching to SWR, you might be tempted to leave parts of the old code around. For example:

const { data, error, isLoading } = useSWR("https://dummyjson.com/todos", fetcher);

if (loading) return <p>Loading...</p>; // ❌ wrong: "loading" doesn’t exist
{todos.map((todo) => ...)}             // ❌ wrong: "todos" comes from old hook
Enter fullscreen mode Exit fullscreen mode

Issues here:

  • The fetcher function was incorrect (.catch dangling).
  • You used loading and todos, which belong to the old custom hook, not SWR.
  • todo.key was used instead of todo.id.

This kind of hybrid setup leads to broken logic.


3. Clean SWR Approach

With SWR, you can remove useEffect, useState, and manual intervals entirely.

import useSWR from "swr";
import axios from "axios";

const fetcher = (url) => axios.get(url).then((res) => res.data);

function App() {
  const { data, error, isLoading } = useSWR(
    "https://dummyjson.com/todos",
    fetcher,
    { refreshInterval: 6000 } // re-fetch every 6 seconds
  );

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Failed to load todos</p>;

  return (
    <>
      {data.todos.map((todo) => (
        <Track todo={todo} key={todo.id} />
      ))}
    </>
  );
}

function Track({ todo }) {
  return (
    <div className="font-black">
      {todo.id} - {todo.todo}
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Why This Is Better

  • Less boilerplate: no need for manual state or intervals.
  • Built-in caching: data stays fresh even when navigating between pages.
  • Automatic revalidation: background fetching keeps your UI up-to-date.
  • Simple polling: just add refreshInterval.

4. Bonus: Custom Hook With SWR

If you still like the useTodos(timeout) style API, you can wrap SWR like this:

function useTodos(timeout) {
  const { data, error, isLoading } = useSWR(
    "https://dummyjson.com/todos",
    fetcher,
    { refreshInterval: timeout * 1000 }
  );

  return {
    todos: data?.todos || [],
    error,
    loading: isLoading,
  };
}
Enter fullscreen mode Exit fullscreen mode

Then use it exactly like before:

const { todos, loading, error } = useTodos(6);
Enter fullscreen mode Exit fullscreen mode

Takeaway: SWR eliminates a lot of repetitive state and effect logic, giving you a cleaner, more declarative way to fetch and manage remote data in React.

Top comments (0)