Design Converter
Education
Last updated on Apr 2, 2025
•7 mins read
Last updated on Mar 31, 2025
•7 mins read
Software Development Executive - I
He writes code, breaks things, fixes them, and then does it all over again!
React has become the go-to choice for modern web applications because of its flexibility and ease of use. That flexibility is especially evident when fetching data from APIs, which is essential for the dynamic information you want to display in your user interface.
In this blog, we'll learn how to do that efficiently in React functional components. We'll use hooks like useState and useEffect and cover best practices for handling loading and error states that inevitably come up. By the end of this post, you'll have a solid grasp of the process and be able to put those practices to work in your React app.
Fetching data from an API is one of the most common operations in modern web development when building interactive applications. With React's component-based architecture, functional components have become the preferred choice due to their simplicity and performance advantages over class components. The data fetching process involves making HTTP requests to an API endpoint and managing the fetched data in the component’s state.
React's hooks, useState and useEffect, allow you to fetch data and efficiently handle a functional component's loading and error states. This is a step-by-step guide to help you master the art of fetching data using React.
To start working with React, you first need a basic setup. You can quickly set up a project for your new app using create-react-app. Here's how to create a new React app:
1npx create-react-app fetch-data-example 2cd fetch-data-example 3npm start
Once the app is set up, you can start implementing data fetching logic inside functional components.
In React, state variables allow us to store and update data. The State hook is essential for this task in functional components. When fetching data from an API, you’ll need to use a state variable to store the data, and another to manage the loading and error states.
Here’s how you can set up the useState hook for managing the fetched data:
1import React, { useState, useEffect } from 'react'; 2 3const DataFetchingComponent = () => { 4 // Define state variables for data, loading, and error states 5 const [data, setData] = useState([]); 6 const [loading, setLoading] = useState(true); 7 const [error, setError] = useState(null); 8 9 // Fetch data from API when the component mounts 10 useEffect(() => { 11 fetchData(); 12 }, []); 13 14 // Function to fetch data 15 const fetchData = async () => { 16 try { 17 const response = await fetch('https://api.example.com/data'); 18 if (!response.ok) { 19 throw new Error('Failed to fetch data'); 20 } 21 const result = await response.json(); 22 setData(result); 23 setLoading(false); 24 } catch (err) { 25 setError(err.message); 26 setLoading(false); 27 } 28 }; 29 30 if (loading) { 31 return <p>Loading...</p>; 32 } 33 34 if (error) { 35 return <p>Error: {error}</p>; 36 } 37 38 return ( 39 <div> 40 <h1>Fetched Data:</h1> 41 <pre>{JSON.stringify(data, null, 2)}</pre> 42 </div> 43 ); 44}; 45 46export default DataFetchingComponent;
State Variables: We define data, loading, and error state variables using useState from React. These manage the fetched data, loading status, and error messages.
API Request: The fetchData function makes the HTTP request to the API endpoint. It updates the data state with the result and sets loading to false if successful. If there's an error, it sets the error state.
Loading and Error States: While data is being fetched, the loading state is set to true, showing a loading message. If there's an error, the error message is displayed.
The useEffect hook is crucial in managing side effects in functional components, such as API calls. When you fetch data, you want the request to be made when the component mounts. This is achieved using useEffect.
In the example above, useEffect calls the fetchData function immediately after the component mounts. It also ensures that the fetch operation occurs only once, as the dependency array is empty.
The dependency array controls when useEffect runs. If it's empty, the effect runs once after the initial render. This is ideal for data fetching. If you have dynamic dependencies, you can add them to the dependency array, causing the effect to run whenever those values change.
1useEffect(() => { 2 fetchData(); 3}, [dependencyVariable]);
Handling errors is an important part of fetching data. In the example above, the catch block in the fetchData function handles any errors that might occur, such as network failures or invalid responses. The error message is then displayed to the user.
Let’s examine the API request's structure in more detail. In the example, the fetch request uses the fetch method native to JavaScript. When making an API request, always check the response's status code to ensure the data was fetched successfully.
1const response = await fetch('https://api.example.com/data'); 2if (!response.ok) { 3 throw new Error('Failed to fetch data'); 4}
This ensures that only successful API responses are processed. If the response is not ok, an error is thrown.
Custom hooks are an excellent way to organize data-fetching logic and make your components more reusable. Extracting the logic into a custom hook allows you to use the same fetching logic across multiple components easily.
1// useFetchData.js 2import { useState, useEffect } from 'react'; 3 4const useFetchData = (url) => { 5 const [data, setData] = useState([]); 6 const [loading, setLoading] = useState(true); 7 const [error, setError] = useState(null); 8 9 useEffect(() => { 10 const fetchData = async () => { 11 try { 12 const response = await fetch(url); 13 if (!response.ok) { 14 throw new Error('Failed to fetch data'); 15 } 16 const result = await response.json(); 17 setData(result); 18 setLoading(false); 19 } catch (err) { 20 setError(err.message); 21 setLoading(false); 22 } 23 }; 24 25 fetchData(); 26 }, [url]); 27 28 return { data, loading, error }; 29}; 30 31export default useFetchData;
In your components, you can now use this custom hook:
1import React from 'react'; 2import useFetchData from './useFetchData'; 3 4const DataDisplayComponent = () => { 5 const { data, loading, error } = useFetchData('https://api.example.com/data'); 6 7 if (loading) { 8 return <p>Loading...</p>; 9 } 10 11 if (error) { 12 return <p>Error: {error}</p>; 13 } 14 15 return ( 16 <div> 17 <h1>Fetched Data:</h1> 18 <pre>{JSON.stringify(data, null, 2)}</pre> 19 </div> 20 ); 21}; 22 23export default DataDisplayComponent;
To better visualize the data-fetching process, here’s a diagram that illustrates how the fetching process occurs in the component:
This diagram outlines the flow of data fetching, from component mounting to handling the response and rendering the appropriate content.
When working with React, fetching data in functional components should be done carefully to ensure smooth user experiences and maintainable code. Some best practices include:
Using useState and useEffect hooks to manage data, loading, and error states.
Handling errors properly by checking the response status and using a try-catch block.
Utilizing custom hooks for better code reusability.
Ensuring cleanup in case the component unmounts before the data fetching is complete.
By following these practices, you can fetch data from APIs efficiently in React and provide a better user experience.
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.