Skip to main content

A Custom Hook for Data Fetching: `useFetch` (Part 1) - Building a Reusable Data Fetching Hook #84

📖 Introduction

Following our exploration of the useToggle hook, this article tackles one of the most common and important use cases for custom hooks: data fetching. We will build a reusable useFetch hook that will simplify how we retrieve data from APIs in our components.


📚 Prerequisites

Before we begin, please ensure you have a solid grasp of the following concepts:

  • React Hooks, especially useState and useEffect.
  • Asynchronous JavaScript (async/await).
  • The Fetch API.

🎯 Article Outline: What You'll Master

In this article, you will learn:

  • The Problem: Understanding the repetitive logic involved in fetching data in React components.
  • Core Implementation: How to build a useFetch hook from scratch.
  • Practical Application: How to use the useFetch hook in a component to fetch and display data.

🧠 Section 1: The Core Concepts of a useFetch Hook

Fetching data in a React component typically involves the following steps:

  1. Managing state for the data, loading status, and any potential errors.
  2. Using the useEffect hook to perform the fetch when the component mounts.
  3. Handling the response and updating the state accordingly.

This logic is often repeated in many components throughout an application. A useFetch hook encapsulates this logic into a single, reusable function.


💻 Section 2: Deep Dive - Implementation and Walkthrough

Let's build the useFetch hook.

2.1 - The useFetch Hook

Create a new file called useFetch.js:

// useFetch.js
import { useState, useEffect } from 'react';

function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const json = await response.json();
setData(json);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};

fetchData();
}, [url]);

return { data, loading, error };
}

export default useFetch;

Step-by-Step Code Breakdown:

  1. import { useState, useEffect } from 'react';: We import the necessary hooks from React.
  2. function useFetch(url) { ... }: We define our custom hook and give it a url parameter.
  3. useState: We use useState to manage the data, loading, and error states.
  4. useEffect: We use useEffect to perform the data fetch. The effect will re-run whenever the url prop changes.
  5. fetchData: Inside the effect, we define an async function to perform the fetch. We use a try...catch...finally block to handle the response, errors, and loading state.
  6. return { data, loading, error };: We return an object containing the data, loading, and error states.

🛠️ Section 3: Project-Based Example: A Component to Display Fetched Data

Now, let's use our new useFetch hook in a component to fetch and display a list of users.

// UserList.js
import React from 'react';
import useFetch from './useFetch';

function UserList() {
const { data: users, loading, error } = useFetch('https://jsonplaceholder.typicode.com/users');

if (loading) {
return <div>Loading...</div>;
}

if (error) {
return <div>Error: {error.message}</div>;
}

return (
<div>
<h1>Users</h1>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}

export default UserList;

Code Breakdown:

  1. import useFetch from './useFetch';: We import our custom hook.
  2. const { data: users, loading, error } = useFetch(...): We call our useFetch hook with the API endpoint and destructure the returned values. We rename data to users for better readability.
  3. We then use the loading and error states to conditionally render loading and error messages.
  4. Finally, we map over the users array to display the list of users.

💡 Conclusion & Key Takeaways

Congratulations! You've created a powerful and reusable useFetch hook. You've seen how custom hooks can encapsulate complex logic, making your components cleaner and more focused on their primary responsibility: rendering the UI.

Let's summarize the key takeaways:

  • Custom hooks are a powerful way to extract and reuse stateful logic, such as data fetching.
  • The useFetch hook simplifies data fetching by managing the data, loading, and error states for you.
  • By creating a useFetch hook, you can make your data fetching logic more consistent and easier to maintain across your application.

Challenge Yourself: To solidify your understanding, try to modify the UserList component to fetch and display a list of posts instead of users.


➡️ Next Steps

You now have a solid understanding of how to create a useFetch hook. In the next article, "A Custom Hook for Data Fetching: useFetch (Part 2)", we will explore how to add more features to our useFetch hook, such as handling request cancellation to prevent memory leaks.

Thank you for your dedication. Stay curious, and happy coding!


glossary

  • useFetch: A custom hook for fetching data from an API.
  • Fetch API: A modern interface that allows you to make HTTP requests to servers from web browsers.
  • async/await: A modern JavaScript feature that allows you to write asynchronous code that looks and behaves like synchronous code.

Further Reading