Difficulty: Beginner
Reading time: ~8 minutes
Example API: https://rickandmortyapi.com
Fetching data is one of the first challenges every React developer faces.
In this article we’ll explore five different approaches—ranging from native browser features to full‑blown data‑fetching libraries—all with the same public API so you can compare them apples‑to‑apples.
Table of Contents
-
Fetch API with
useEffect
-
Axios with
useEffect
-
A Reusable
useFetch
Hook - React Query (TanStack Query)
- SWR (stale‑while‑revalidate)
- Choosing the Right Tool
- Wrapping Up
1. Fetch API with useEffect
The fastest way to pull data is the browser‑native fetch
wrapped in a useEffect
hook:
import React, { useState, useEffect } from "react";
function CharacterListFetch() {
const [characters, setCharacters] = useState([]);
useEffect(() => {
fetch("https://rickandmortyapi.com/api/character")
.then((res) => res.json())
.then((data) => setCharacters(data.results))
.catch((err) => console.error("Error:", err));
}, []);
return (
<>
<h2>Characters (Fetch API)</h2>
<ul>
{characters.map((c) => (
<li key={c.id}>{c.name}</li>
))}
</ul>
</>
);
}
Pros & Cons
✅ Pros | ⚠️ Cons |
---|---|
Zero dependencies | Manual parsing (res.json() ) |
Perfect for tiny demos | Must handle caching, retries, etc. yourself |
2. Axios with useEffect
Axios offers a nicer API and extra features (interceptors, cancellation…).
import React, { useState, useEffect } from "react";
import axios from "axios";
function CharacterListAxios() {
const [characters, setCharacters] = useState([]);
useEffect(() => {
axios
.get("https://rickandmortyapi.com/api/character")
.then((res) => setCharacters(res.data.results)) // already JSON
.catch((err) => console.error("Error:", err));
}, []);
return (
<>
<h2>Characters (Axios)</h2>
<ul>
{characters.map((c) => (
<li key={c.id}>{c.name}</li>
))}
</ul>
</>
);
}
3. A Reusable useFetch
Hook
When you start duplicating fetch logic, extract it into a custom hook.
// useFetch.js
import { useState, useEffect } from "react";
export function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
setError(null);
fetch(url)
.then((res) => res.json())
.then((json) => {
setData(json);
setLoading(false);
})
.catch((err) => {
setError(err);
setLoading(false);
});
}, [url]);
return { data, loading, error };
}
Usage:
import { useFetch } from "./useFetch";
function CharacterListHook() {
const { data, loading, error } = useFetch(
"https://rickandmortyapi.com/api/character"
);
if (loading) return <p>Loading…</p>;
if (error) return <p>Error loading data</p>;
return (
<>
<h2>Characters (Custom Hook)</h2>
<ul>
{data.results.map((c) => (
<li key={c.id}>{c.name}</li>
))}
</ul>
</>
);
}
4. React Query (TanStack Query)
For production apps with complex data flows, React Query is a powerhouse.
npm install @tanstack/react-query
import { QueryClient, QueryClientProvider, useQuery } from "@tanstack/react-query";
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<CharacterListReactQuery />
</QueryClientProvider>
);
}
function CharacterListReactQuery() {
const { data, error, isLoading } = useQuery({
queryKey: ["characters"],
queryFn: async () =>
(await fetch("https://rickandmortyapi.com/api/character")).json(),
});
if (isLoading) return <p>Loading…</p>;
if (error) return <p>Error loading data</p>;
return (
<>
<h2>Characters (React Query)</h2>
<ul>
{data.results.map((c) => (
<li key={c.id}>{c.name}</li>
))}
</ul>
</>
);
}
Highlights
- Built‑in caching & background refetch
- Stale‑time tuning, pagination, mutations
- Devtools for debugging
5. SWR (stale‑while‑revalidate)
Vercel’s SWR is minimal but mighty, ideal for Next.js projects.
npm install swr
import useSWR from "swr";
const fetcher = (url) => fetch(url).then((res) => res.json());
function CharacterListSWR() {
const { data, error, isLoading } = useSWR(
"https://rickandmortyapi.com/api/character",
fetcher
);
if (isLoading) return <p>Loading…</p>;
if (error) return <p>Error loading data</p>;
return (
<>
<h2>Characters (SWR)</h2>
<ul>
{data.results.map((c) => (
<li key={c.id}>{c.name}</li>
))}
</ul>
</>
);
}
6. Choosing the Right Tool
Scenario | Recommended Approach |
---|---|
Small tutorial, no extra deps | Fetch API |
Need features like interceptors | Axios |
Reusing simple fetch logic | Custom hook |
Large‑scale app, mutations, caching | React Query |
Lightweight cache, Next.js | SWR |
Tip: You can mix & match—e.g., use Fetch for one‑offs and React Query for critical data.
7. Wrapping Up
That’s it! You now have five practical patterns for fetching data in React:
- Fetch API
- Axios
- Custom hook
- React Query
- SWR
Pick the one that suits your project’s complexity and your team’s preferences.
Questions or feedback? Drop a comment below—let’s chat!
If this post helped you, please ❤️ like, 🦄 unicorn, or 📣 share it with your fellow devs.
Happy coding!
Top comments (0)