In modern web applications, interacting with APIs is essential. APIs allow you to fetch, add, update, and delete data on servers, making your application dynamic and interactive.
In this blog, we’ll cover how to use the Fetch API in React to perform these actions with ease. We’ll look at making GET, POST, PUT, and DELETE requests, handling errors, and using async-await syntax for better readability.
Also Read: How To Use Axios With React: The Definitive Guide
For this tutorial, we’ll use the JSONPlaceholder API, which provides sample data for testing and learning purposes.
How to Make a GET Request
The GET
request is used to retrieve data from an API. In React, we can use the fetch()
function to make this request. Let’s create a function that fetches a list of posts.
GetRequest.jsx
import { useState, useEffect } from 'react'; const GetRequest = () => { const [posts, setPosts] = useState([]); useEffect(() => { fetch("https://jsonplaceholder.typicode.com/posts") .then(response => response.json()) // Parse JSON from response .then(data => setPosts(data)) // Update state with data .catch(error => console.error("Error fetching posts:", error)); }, []); return ( <div> <h1>Posts</h1> <ul> {posts.map(post => ( <li key={post.id}>{post.title}</li> ))} </ul> </div> ); }; export default GetRequest;
Explanation
fetch(): Initiates the GET request to the API.
.then(response => response.json()): Converts the response to JSON format.
.then(data => setPosts(data)): Stores the data in the state variable posts.
.catch(error => console.error(…)): Catches and logs any errors that occur.
Output:
How to Make a POST Request
POST
requests are used to send new data to the server. Here, we’ll add a new post. For POST
, we also need to include headers and a body containing the new data.
This example component will fetch a list of posts on load and allow you to add a new post by clicking a button.
PostRequest.jsx
import { useState, useEffect } from 'react'; const PostRequest = () => { const [posts, setPosts] = useState([]); const [newPostTitle, setNewPostTitle] = useState(''); const [newPostBody, setNewPostBody] = useState(''); // Fetch posts when the component mounts useEffect(() => { fetch("https://jsonplaceholder.typicode.com/posts") .then(response => response.json()) .then(data => setPosts(data)) .catch(error => console.error("Error fetching posts:", error)); }, []); // Function to handle adding a new post const handleAddPost = () => { fetch("https://jsonplaceholder.typicode.com/posts", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ title: newPostTitle, body: newPostBody, userId: 1, }), }) .then(response => response.json()) .then(newPost => { setPosts([...posts, newPost]); setNewPostTitle(''); // Clear the input fields setNewPostBody(''); }) .catch(error => console.error("Error adding post:", error)); }; return ( <div> <h1>Posts</h1> <ul> {posts.map(post => ( <li key={post.id}> <h2>{post.title}</h2> <p>{post.body}</p> </li> ))} </ul> <h2>Add New Post</h2> <input type="text" placeholder="Post Title" value={newPostTitle} onChange={(e) => setNewPostTitle(e.target.value)} /> <textarea placeholder="Post Body" value={newPostBody} onChange={(e) => setNewPostBody(e.target.value)} /> <button onClick={handleAddPost}>Add Post</button> </div> ) } export default PostRequest
Explanation
useState
Hooks: Used to manage state for posts, the new post title, and the new post body.useEffect
: Fetches posts when the component mounts and stores them in theposts
state.handleAddPost
Function: Sends a POST request to add a new post.method: "POST"
: Specifies the HTTP method.headers
: SetsContent-Type
to"application/json"
to let the API know we’re sending JSON data.body
: Contains the data we want to send in JSON format..then(newPost => setPosts([...posts, newPost]))
: Adds the newly created post to the current list of posts.
- Form Inputs: Allows users to enter a new post title and body.
This example shows how to combine fetching and adding new posts in a React component, making it easy to extend for further CRUD operations.
Output:
How to Make a PUT Request
PUT
requests update an existing resource. Here’s a complete example in React that shows how to use a PUT request to update an existing post using fetch()
and JSON.stringify()
.
This example includes fetching posts, selecting a post to edit, and updating the post by sending a PUT request.
PutRequest.jsx
import { useState, useEffect } from 'react'; const PutRequest = () => { const [posts, setPosts] = useState([]); const [editPostId, setEditPostId] = useState(null); const [editTitle, setEditTitle] = useState(''); const [editBody, setEditBody] = useState(''); // Fetch posts when the component mounts useEffect(() => { fetch("https://jsonplaceholder.typicode.com/posts") .then(response => response.json()) .then(data => setPosts(data)) .catch(error => console.error("Error fetching posts:", error)); }, []); // Function to handle selecting a post for editing const handleEdit = (post) => { setEditPostId(post.id); setEditTitle(post.title); setEditBody(post.body); }; // Function to handle updating a post const handleUpdatePost = () => { fetch(`https://jsonplaceholder.typicode.com/posts/${editPostId}`, { method: "PUT", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ id: editPostId, title: editTitle, body: editBody, userId: 1, }), }) .then(response => response.json()) .then(updatedPost => { // Update the post in the local state setPosts(posts.map(post => post.id === editPostId ? updatedPost : post )); // Clear edit fields setEditPostId(null); setEditTitle(''); setEditBody(''); }) .catch(error => console.error("Error updating post:", error)); }; return ( <div> <h1>Posts</h1> <ul> {posts.map(post => ( <li key={post.id}> <h2>{post.title}</h2> <p>{post.body}</p> <button onClick={() => handleEdit(post)}>Edit</button> </li> ))} </ul> {editPostId && ( <div> <h2>Edit Post</h2> <input type="text" value={editTitle} onChange={(e) => setEditTitle(e.target.value)} placeholder="Edit Title" /> <textarea value={editBody} onChange={(e) => setEditBody(e.target.value)} placeholder="Edit Body" /> <button onClick={handleUpdatePost}>Update Post</button> </div> )} </div> ) } export default PutRequest
Explanation
- State Variables:
posts
: Holds the list of posts.editPostId
,editTitle
, andeditBody
: Used to manage the currently selected post for editing.
useEffect
Hook:- Fetches the posts from the API on component mount.
handleEdit
Function:- Sets the selected post’s ID, title, and body to allow editing.
handleUpdatePost
Function:- Sends a PUT request to update the selected post.
- Uses
JSON.stringify()
to convert the updated data to a JSON string. - After receiving the updated post from the server, it updates the post in the
posts
array. - Clears the edit fields after updating.
- Edit Form:
- Appears when a post is selected for editing.
- Includes an input for the post title and a textarea for the post body.
Output:
How to Make a DELETE Request
DELETE
requests remove an item from the server. Here’s a full example of a React component that includes fetching, adding, and deleting posts using fetch()
with JSONPlaceholder.
This example demonstrates how to make a DELETE
request to remove a post.
DeleteRequest.jsx
import { useState, useEffect } from 'react'; const DeleteRequest = () => { const [posts, setPosts] = useState([]); const [newPostTitle, setNewPostTitle] = useState(''); const [newPostBody, setNewPostBody] = useState(''); // Fetch posts when the component mounts useEffect(() => { fetch("https://jsonplaceholder.typicode.com/posts") .then(response => response.json()) .then(data => setPosts(data)) .catch(error => console.error("Error fetching posts:", error)); }, []); // Function to handle adding a new post const handleAddPost = () => { fetch("https://jsonplaceholder.typicode.com/posts", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ title: newPostTitle, body: newPostBody, userId: 1, }), }) .then(response => response.json()) .then(newPost => { setPosts([...posts, newPost]); setNewPostTitle(''); // Clear the input fields setNewPostBody(''); }) .catch(error => console.error("Error adding post:", error)); }; // Function to handle deleting a post const handleDeletePost = (postId) => { fetch(`https://jsonplaceholder.typicode.com/posts/${postId}`, { method: "DELETE", }) .then(response => { if (response.ok) { setPosts(posts.filter(post => post.id !== postId)); } else { console.error("Error deleting post:", response.statusText); } }) .catch(error => console.error("Error deleting post:", error)); }; return ( <div> <h1>Posts</h1> <ul> {posts.map(post => ( <li key={post.id}> <h2>{post.title}</h2> <p>{post.body}</p> <button onClick={() => handleDeletePost(post.id)}>Delete</button> </li> ))} </ul> <h2>Add New Post</h2> <input type="text" placeholder="Post Title" value={newPostTitle} onChange={(e) => setNewPostTitle(e.target.value)} /> <textarea placeholder="Post Body" value={newPostBody} onChange={(e) => setNewPostBody(e.target.value)} /> <button onClick={handleAddPost}>Add Post</button> </div> ) } export default DeleteRequest
Explanation
- State Variables:
posts
: Holds the list of posts.newPostTitle
andnewPostBody
: Used to store input values for a new post.
useEffect
Hook:- Fetches posts from the API when the component mounts.
handleAddPost
Function:- Sends a POST request to add a new post to the API.
- Uses
JSON.stringify()
to convert the new post data into JSON format for the request. - Updates the
posts
state with the new post.
handleDeletePost
Function:- Sends a DELETE request to remove a post with the specified
postId
. - If the response is successful (
response.ok
), it filters out the deleted post from theposts
state.
- Sends a DELETE request to remove a post with the specified
- UI Elements:
- Displays a list of posts, each with a “Delete” button to remove it.
- Provides input fields and a button for adding a new post.
Output:
How to Handle Errors with Fetch API
Handling errors helps ensure a smooth user experience. You can handle errors by adding a .catch()
block to each fetch call.
Here’s a complete React example demonstrating how to handle errors effectively when using the Fetch API. In this example, we will add error handling for both fetching and adding posts. If an error occurs, it will display an error message to the user.
HandleErrors.jsx
import { useState, useEffect } from 'react'; const HandleErrors = () => { const [posts, setPosts] = useState([]); const [newPostTitle, setNewPostTitle] = useState(''); const [newPostBody, setNewPostBody] = useState(''); const [error, setError] = useState(null); // Fetch posts with error handling useEffect(() => { fetch("https://jsonplaceholder.typicode.com/posts") .then(response => { if (!response.ok) { throw new Error(`Error fetching posts: ${response.statusText}`); } return response.json(); }) .then(data => setPosts(data)) .catch(error => setError(error.message)); }, []); // Function to handle adding a new post with error handling const handleAddPost = () => { fetch("https://jsonplaceholder.typicode.com/posts", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ title: newPostTitle, body: newPostBody, userId: 1, }), }) .then(response => { if (!response.ok) { throw new Error(`Error adding post: ${response.statusText}`); } return response.json(); }) .then(newPost => { setPosts([...posts, newPost]); setNewPostTitle(''); // Clear the input fields setNewPostBody(''); setError(null); // Clear any existing error }) .catch(error => setError(error.message)); }; return ( <div> <h1>Posts</h1> {/* Display error message if any */} {error && <p style={{ color: 'red' }}>{error}</p>} <ul> {posts.map(post => ( <li key={post.id}> <h2>{post.title}</h2> <p>{post.body}</p> </li> ))} </ul> <h2>Add New Post</h2> <input type="text" placeholder="Post Title" value={newPostTitle} onChange={(e) => setNewPostTitle(e.target.value)} /> <textarea placeholder="Post Body" value={newPostBody} onChange={(e) => setNewPostBody(e.target.value)} /> <button onClick={handleAddPost}>Add Post</button> </div> ) } export default HandleErrors
Explanation of Error Handling
- Error State:
- The
error
state variable is used to store any error messages that occur. This way, we can display error messages directly in the UI.
- The
- Error Handling in Fetch Request (GET):
- After the
fetch()
call, we check if the response is OK withresponse.ok
. If not, we throw an error with a custom message that includesresponse.statusText
. - This error is then caught in the
.catch()
block, where we set the error message to be displayed.
- After the
- Error Handling in POST Request:
- Similarly, we check if the response for the POST request is OK. If it’s not, we throw an error with a descriptive message.
- The
.catch()
block will set the error message to be shown to the user if there is an issue with adding the new post.
- Displaying Error Messages:
- We conditionally render the error message in the UI using
{error && <p style={{ color: 'red' }}>{error}</p>}
. If an error occurs, it will appear in red text at the top of the component.
- We conditionally render the error message in the UI using
Output:
Using Async-Await Syntax with Fetch API
The async-await syntax makes the code more readable, especially when chaining multiple asynchronous calls.
Here’s a full example of a React component that demonstrates using async-await syntax with the Fetch API. This example includes fetching posts, adding a new post, and handling errors with try-catch
blocks to make the code cleaner and easier to read.
AsyncAwait.jsx
import { useState, useEffect } from 'react'; const AsyncAwait = () => { const [posts, setPosts] = useState([]); const [newPostTitle, setNewPostTitle] = useState(''); const [newPostBody, setNewPostBody] = useState(''); const [error, setError] = useState(null); // Fetch posts using async-await and error handling useEffect(() => { const fetchPosts = async () => { try { const response = await fetch("https://jsonplaceholder.typicode.com/posts"); if (!response.ok) { throw new Error(`Error fetching posts: ${response.statusText}`); } const data = await response.json(); setPosts(data); } catch (error) { setError(error.message); } }; fetchPosts(); }, []); // Function to handle adding a new post with async-await and error handling const handleAddPost = async () => { try { const response = await fetch("https://jsonplaceholder.typicode.com/posts", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ title: newPostTitle, body: newPostBody, userId: 1, }), }); if (!response.ok) { throw new Error(`Error adding post: ${response.statusText}`); } const newPost = await response.json(); setPosts([...posts, newPost]); setNewPostTitle(''); // Clear the input fields setNewPostBody(''); setError(null); // Clear any existing error } catch (error) { setError(error.message); } }; return ( <div> <h1>Posts</h1> {/* Display error message if any */} {error && <p style={{ color: 'red' }}>{error}</p>} <ul> {posts.map(post => ( <li key={post.id}> <h2>{post.title}</h2> <p>{post.body}</p> </li> ))} </ul> <h2>Add New Post</h2> <input type="text" placeholder="Post Title" value={newPostTitle} onChange={(e) => setNewPostTitle(e.target.value)} /> <textarea placeholder="Post Body" value={newPostBody} onChange={(e) => setNewPostBody(e.target.value)} /> <button onClick={handleAddPost}>Add Post</button> </div> ) } export default AsyncAwait
Explanation of Async-Await Usage
- Fetching Posts with Async-Await:
- Inside
useEffect
, we define anasync
function calledfetchPosts
. await fetch("https://jsonplaceholder.typicode.com/posts")
waits for the response.- If the response isn’t OK (
!response.ok
), it throws an error, which is caught in thecatch
block. - The
await response.json()
parses the data, which is then set to theposts
state.
- Inside
- Adding a New Post with Async-Await:
- The
handleAddPost
function is anasync
function that makes a POST request to add a new post. await fetch(...)
sends the POST request, and if it’s successful, the new post data is added to the state.- If there’s an error, it’s caught in the
catch
block, and the error message is shown in the UI.
- The
- Error Display:
- The error message is stored in
error
state and displayed if present.
- The error message is stored in
Conclusion
Using the Fetch API in React is straightforward and powerful for interacting with external APIs.
You’ve learned how to perform GET
, POST
, PUT
, and DELETE
requests, handle errors, and use async-await syntax to write clean, readable code. Now, you can confidently consume and manipulate data in your React applications.
Add comment