How to Use Fetch API in React
This article provides a straightforward, step-by-step guide on how to
implement the Fetch API in React to retrieve data from an external API.
You will learn how to make HTTP requests inside functional components
using the useEffect hook, manage component state for the
fetched data, and handle loading and error states effectively.
The Basic Fetch Implementation
To fetch data in a React functional component, you combine the
useState hook (to store the data) and the
useEffect hook (to trigger the request when the component
mounts).
Here is a complete, minimal example of fetching data from a public API:
import React, { useState, useEffect } from 'react';
function DataFetchingComponent() {
const [data, setData] = useState([]);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then((response) => response.json())
.then((data) => setData(data))
.catch((error) => console.error('Error fetching data:', error));
}, []); // Empty dependency array means this runs once on mount
return (
<div>
<h1>Fetched Posts</h1>
<ul>
{data.slice(0, 5).map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
export default DataFetchingComponent;Handling Loading and Error States
In real-world applications, network requests take time and can fail. You must manage loading and error states to provide a good user experience.
Using async/await syntax makes the asynchronous code
cleaner and easier to read:
import React, { useState, useEffect } from 'react';
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUsers = async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setUsers(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchUsers();
}, []);
if (loading) return <p>Loading users...</p>;
if (error) return <p>Error: {error}</p>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default UserList;Best Practice: Cleaning Up Requests
If a component unmounts before the fetch request completes, it can
cause memory leaks or state updates on unmounted components. To prevent
this, use an AbortController to cancel the request if the
component unmounts.
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
const fetchData = async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users', { signal });
const data = await response.json();
setUsers(data);
} catch (err) {
if (err.name !== 'AbortError') {
setError(err.message);
}
}
};
fetchData();
// Cleanup function to abort the fetch if the component unmounts
return () => {
controller.abort();
};
}, []);