Introduction
React applications commonly fetch data from APIs to display dynamic content. One of the most popular ways to make HTTP requests in React is by using the Fetch API.
The Fetch API is a built-in JavaScript feature used to send and receive data from servers. It works with promises and provides an easy way to handle asynchronous requests.
In React, the Fetch API is commonly used with:
- useEffect
- useState
- Async/Await
- REST APIs
What is Fetch API?
Fetch API is a modern JavaScript API used to make HTTP requests.
It allows applications to:
- Fetch data from servers
- Send data to APIs
- Update server data
- Delete data
Fetch API returns a Promise.
Why Fetch API is Used in React?
React applications are usually connected to backend servers or external APIs.
Examples:
- Fetching products from an e-commerce API
- Loading blog posts
- Displaying user profiles
- Sending login data
Fetch API helps React applications communicate with servers dynamically.
Features of Fetch API
1. Built into JavaScript
No external library required.
2. Promise-Based
Supports asynchronous programming.
3. Supports All HTTP Methods
- GET
- POST
- PUT
- DELETE
- PATCH
4. Cleaner Syntax
Modern and readable code.
5. Works Well with React Hooks
Commonly used with:
- useEffect
- useState
Basic Syntax of Fetch API
fetch(url)
.then((response) => response.json())
.then((data) => {
console.log(data);
});
Understanding the Syntax
| Part | Description |
|---|---|
| fetch(url) | Sends request |
| response.json() | Converts response into JSON |
| then() | Handles successful response |
| catch() | Handles errors |
Making a GET Request in React
GET requests are used to fetch data from APIs.
Example: Fetch Users Data
App.js
import { useEffect, useState } from "react";
function App() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => response.json())
.then((data) => {
setUsers(data);
});
}, []);
return (
<div>
<h1>User List</h1>
{users.map((user) => (
<p key={user.id}>
{user.name}
</p>
))}
</div>
);
}
export default App;
How This Example Works?
Step 1
useEffect() runs after component renders.
Step 2
Fetch API sends request to server.
Step 3
response.json() converts response into JSON format.
Step 4
setUsers(data) updates state.
Step 5
React re-renders UI with new data.
Using Async/Await with the Fetch API
Async/Await provides cleaner syntax.
Example
import { useEffect, useState } from "react";
function App() {
const [posts, setPosts] = useState([]);
useEffect(() => {
const fetchPosts = async () => {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts"
);
const data = await response.json();
setPosts(data);
};
fetchPosts();
}, []);
return (
<div>
<h1>Posts</h1>
{posts.map((post) => (
<p key={post.id}>
{post.title}
</p>
))}
</div>
);
}
export default App;
Fetch API with Loading State
Loading states improve user experience.
Example
import { useEffect, useState } from "react";
function App() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => response.json())
.then((data) => {
setUsers(data);
setLoading(false);
});
}, []);
if (loading) {
return <h1>Loading...</h1>;
}
return (
<div>
{users.map((user) => (
<p key={user.id}>
{user.name}
</p>
))}
</div>
);
}
export default App;
Error Handling with Fetch API
Always handle API errors properly.
Example
import { useEffect, useState } from "react";
function App() {
const [data, setData] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
fetch("https://invalid-api.com/data")
.then((response) => response.json())
.then((result) => {
setData(result);
})
.catch((err) => {
setError("Something went wrong");
});
}, []);
return (
<div>
{error && <h1>{error}</h1>}
</div>
);
}
export default App;
POST Request Using Fetch API
POST requests send data to servers.
Example
const addUser = async () => {
const response = await fetch(
"https://jsonplaceholder.typicode.com/users",
{
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
name: "John"
})
}
);
const data = await response.json();
console.log(data);
};
Common HTTP Methods
| Method | Purpose |
|---|---|
| GET | Fetch data |
| POST | Create data |
| PUT | Update data |
| DELETE | Remove data |
| PATCH | Partial update |
Fetch API with useEffect
Fetch requests are usually placed inside useEffect().
Reason:
- Prevents infinite rendering
- Runs after component mount
Incorrect Example
fetch("api/data");
inside component body.
This causes repeated API calls.
Correct Example
useEffect(() => {
fetch("api/data");
}, []);
Real-Life Example: Product List
Example
import { useEffect, useState } from "react";
function Products() {
const [products, setProducts] = useState([]);
useEffect(() => {
const fetchProducts = async () => {
const response = await fetch(
"https://fakestoreapi.com/products"
);
const data = await response.json();
setProducts(data);
};
fetchProducts();
}, []);
return (
<div>
<h1>Products</h1>
{products.map((product) => (
<div key={product.id}>
<h2>{product.title}</h2>
<p>${product.price}</p>
</div>
))}
</div>
);
}
export default Products;
Advantages of Fetch API
1. Native JavaScript Support
No additional package needed.
2. Modern Syntax
Cleaner than older APIs like XMLHttpRequest
3. Promise Support
Easy asynchronous handling.
4. Flexible
Supports all request types.
5. Easy Integration with React
Works perfectly with hooks.
Fetch API vs Axios
| Feature | Fetch API | Axios |
|---|---|---|
| Built into JavaScript | Yes | No |
| External Package Needed | No | Yes |
| JSON Conversion | Manual | Automatic |
| Request Cancellation | Limited | Better |
| Simplicity | Moderate | Easy |
Common Mistakes
1. Forgetting response.json()
Wrong:
const data = response;
Correct:
const data = await response.json();
2. Missing Dependency Array
Wrong:
useEffect(() => {
fetchData();
});
Causes infinite API calls.
Correct:
useEffect(() => {
fetchData();
}, []);
3. Not Handling Errors
Always use .catch() or try-catch.
4. Updating State Incorrectly
Wrong:
setUsers(response);
Correct:
setUsers(data);
5. Forgetting Loading State
Users should know data is loading.
Best Practices
Use Async/Await
Cleaner and easier to read.
Handle Errors Properly
Always show error messages.
Use Loading States
Improves user experience.
Keep API Logic Organized
Separate API functions if needed.
Avoid Unnecessary API Calls
Use dependency arrays correctly.
Advanced Example with Try-Catch
import { useEffect, useState } from "react";
function App() {
const [users, setUsers] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUsers = async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/users"
);
const data = await response.json();
setUsers(data);
} catch (err) {
setError("Failed to fetch data");
}
};
fetchUsers();
}, []);
return (
<div>
{error && <h1>{error}</h1>}
{users.map((user) => (
<p key={user.id}>
{user.name}
</p>
))}
</div>
);
}
export default App;
Conclusion
Fetch API is one of the most important tools for React developers. It allows React applications to communicate with servers and display dynamic data efficiently.
Using Fetch API with React hooks like:
- useEffect
- useState