codedamn

How to use fetch in React? Fetch data from APIs in React.js

  • Varun Tiwari's profile image
    Varun Tiwari
    Author
How to use fetch in  React? Fetch data from APIs in React.js

We all know React makes it very easy to work with dynamic content in our application. We store some information in the database, and when we load our application, we fetch those details from the database and render them on the DOM.

When it comes to fetching data in React.js, there are a lot of different patterns we can follow. But which one to consider? That depends on the project’s scale. It may be necessary to use a more complex setup for a large project, whereas for a smaller project, a simpler approach may suffice.

This article will discuss a basic approach to properly fetch and display information from an API in a React.js based application.

Using the browser’s native fetch API

The fetch API is provided by the browsers, which provide an interface for fetching the resources across the network. It offers more features than the conventional XMLHttpRequest.

For this tutorial, we’ll use a fake REST API provided by jsonplaceholder.typicode.com. It provides a variety of fake REST APIs such as posts, photos, users, todos, etc. To keep things simple, we’ll go with the todos API.

We can access the API endpoint at: https://jsonplaceholder.typicode.com/todos.

On making a GET request to the above URL, we’ll get a list of todos as a response,

Response from todos API

Each todo will have the following fields,

{ userId: Number, // The ID of the user who made the Todo id: Number, // The ID of the Todo title: String, // The title of the Todo completed: Boolean // The status of the Todo }
Code language: JSON / JSON with Comments (json)

Now, to fetch all the todos to our React.js application, we’ll follow the given flow,

Steps to fetch data from an API

Using fetch() to send a request to the API endpoint

The fetch() API is natively supported by almost every browser nowadays (except for some older browsers). To send an HTTP request we simply use the fetch() function with the API endpoint URL as an argument. We receive a promise that resolves to the actual data from the API,

const response = await fetch('https://api.example.com');
Code language: JavaScript (javascript)

Optionally, we can pass a second argument that contains the options we might want while sending an HTTP request. For example, we can specify the HTTP method we are using,

const response = await fetch('https://api.example.com', { method: 'POST' // Sending a POST request });
Code language: JavaScript (javascript)

Now, let’s write some code. Here, we have a simple React.js application with just one heading,

Simple React App

Now, we write an asynchronous function that fetches the list of todos from the API and logs the response,

const url = 'https://jsonplaceholder.typicode.com/todos'; const fetchTodos = async () => { try { const response = await fetch(url); console.log(response); } catch (error) { console.log(error); } };
Code language: JavaScript (javascript)

When we run the above function, we can see that we are getting some response from the API,

Response generated by fetch()

To get the desired data, i.e., the list of todos, we’ll parse the response and get the response body as JSON,

const url = 'https://jsonplaceholder.typicode.com/todos'; const fetchTodos = async () => { try { const response = await fetch(url); const data = await response.json(); console.log(data); } catch (error) { console.log(error); } };
Code language: JavaScript (javascript)

Now, if we run the function, we can see the list of todos being logged,

List of todos returned by the API

Storing the list of todos in a state variable

Since we now have the data, let’s store it in a React.js state variable. We first create a state which initially contains an empty array,

const [todos, setTodos] = React.useState([]);
Code language: JavaScript (javascript)

Now, instead of logging the data, we’ll store it in our todos state,

const fetchTodos = async () => { try { const response = await fetch(url); const data = await response.json(); setTodos(data); } catch (error) { console.log(error); } };
Code language: JavaScript (javascript)

Now, let’s log our state and see if everything works fine,

Infinite loop issue

We can see the logs of our todos state variable. But if we notice carefully, we can observe that the logs keep coming. Also, if we inspect the network tab, we can see that we keep fetching from the API one after the other. This is a massive issue in our code.

But why is this happening?

The answer lies in the working of state variables in React.js. The component re-renders whenever there is a change in the state. In our case, every time we call the fetchTodos function, we change the state of the todos variable. As soon as we change the state of the todos variable, the component re-renders and calls the fetchTodos function again. Our application ends in an infinite loop of fetching the todos and updating the state.

How to solve this issue? We’ll use the useEffect() hook provided by React.js to solve this issue.

Using useEffect to prevent infinite re-render loop

The useEffect() hook is used to run a side-effect, every time the component’s state changes. It takes a callback function as its first argument. This callback function will get called when the state of the component is changed.

// Callback will be called on every re-render. useEffect(() => { // Do something })
Code language: JavaScript (javascript)

Optionally, we can provide a dependency array as the second argument. The dependency array can be either empty or can contain some state variables.

If the dependency array is empty, the callback function will get called only once when the component is mounted and won’t be called on any state changes.

// Callback will be called every time the component mounts. useEffect(() => { // Do something }, [])
Code language: JavaScript (javascript)

Else, suppose we provide some state variables in the dependency array. In that case, the callback function will get called when the component mounts or any state in the dependency array changes.

// Callback will be called: // 1. Every time the component mounts. // 2. Every time the state1 or state2 changes. useEffect(() => { // Do something }, [state1, state2])
Code language: JavaScript (javascript)

Now, we can use the useEffect() hook to properly call the fetchTodos() function. We will call the fetchTodos() function inside the useEffect() callback, and since we want to fetch the list of todos only once, we will provide an empty dependency array,

const fetchTodos = async () => { try { const response = await fetch(url); const data = await response.json(); setTodos(data); console.log(data); } catch (error) { console.log(error); } }; useEffect(() => { fetchTodos(); }, [])
Code language: JavaScript (javascript)

Now, if we look at the console, we can see that the list of todos is logged only once,

Correctly fetching the liist of todos

Displaying the list of todos on the screen

Now, since we have stored the list of todos in our todos state variable, we can now display them on screen. To display the list of todos, we’ll iterate over each element of the list using map() and then return some JSX containing the to-do item details.

The map() function accepts a callback function as an argument. This callback function will be called with each element of the todos array. Whatever we return from the callback function will be pushed inside a new array, and this new array is returned from map().

todos.map((todoItem) => { return ( <article> <h4>{todoItem.title}</h4> <p>status: {todoItem.completed ? 'Yes' : 'No'}</p> </article> ); })
Code language: JavaScript (javascript)

We can render the new list returned by the map() function inside our component,

return ( <main> <header> <h1>Todos</h1> </header> <section> {todos.map((todoItem) => { return ( <article> <h4>{todoItem.title}</h4> <p>status: {todoItem.completed ? 'Yes' : 'No'}</p> </article> ); })} </section> </main> );
Code language: JavaScript (javascript)

As soon as we make this change, we’ll be able to see the list of todos in our application,

Displaying the list of todos on the screen

Woohoo 🥳 We successfully fetched the list of todos and displayed them on the screen.

Here we used a straightforward API but could have also used a more complex one. The steps to fetch and render the data on the screen would still be the same.

Bonus

Now that we are comfortable fetching data using the browser’s native fetch() API, we can look at a better alternative. Axios is a promise-based HTTP client library that we can use to make HTTP requests with more flexibility.

One of the major advantages of Axios over native fetch() is backward compatibility. Axios uses the XMLHttpRequest behind the scene to make the HTTP request. This makes Axios compatible with some older web browsers like IE11 (fetch() doesn’t work on IE11). Another nice feature of Axios is Error Handling. Unlike fetch(), Axios makes it very easy to handle errors based on HTTP status codes. Also, with Axios, we don’t need to convert the response to JSON. It automatically does it for us after fetching the data.

You can install the Axios library from NPM by using the following command in your project directory,

npm install axios
Code language: Bash (bash)

Let’s now try to fetch data from the same todos API we used earlier,

import axios from 'axios'; const url = 'https://jsonplaceholder.typicode.com/todos'; const fetchTodos = async () => { try { const response = await axios(url); console.log(response); } catch (error) { console.log(error); } };
Code language: JavaScript (javascript)

In the console, we can see the response object,

Response object returned by Axios

Also, with Axios, the data will be contained in the data key of the response object. Now, let’s replace the new fetchTodos() function with the old one in our App component,

Fetching and rendering the list of todos using Axios

As you can see, we still see the list of todos on the screen. Please note that, since Axios puts all the data we get from the API on the data key of the response object, we must set the todos state using,

setTodos(response.data);
Code language: JavaScript (javascript)

Quick Tip

The HTTP request is typically enclosed in a try-catch block to catch any potential errors that may occur during the sending of the request or errors generated by the API,

try { const response = await axios(url); } catch(error) { console.log(error) }
Code language: JavaScript (javascript)

You can access the error message sent by the API from the error object we have access to in the catch block. Axios places the error information sent by the API on the response key of the error object. It contains information such as headers, status, etc.

error { . . response: { // Error information sent by the API } }
Code language: JavaScript (javascript)

You can access the error payload sent by the API using,

error.response.data
Code language: JavaScript (javascript)

Conclusion

In this article, we learned how to make HTTP requests to some API endpoints and retrieve the required data using the browser’s native fetch() API. Then, we learned how to use fetch() properly with React.js in order to request some information from an API and render it on the screen. Additionally, we looked at Axios, which is a better alternative to fetch().

Thank you so much for reading 😄

Learn programming on codedamn

Codedamn is an interactive coding platform with tons of sweet programming courses that can help you land your first coding job. Here's how:

Programming is one of the most in-demand jobs today. Learning to program can change your future. All the best!