Fetch API in React

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

  1. GET
  2. POST
  3. PUT
  4. DELETE
  5. 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

Continue Learning React