DEV Community

Cover image for Building Your Own Custom Hook in React: A Simple Guide to Fetching Data
Hammad Afzal
Hammad Afzal

Posted on

Building Your Own Custom Hook in React: A Simple Guide to Fetching Data

1. Introduction

When working with React, managing state and side effects — like fetching data — can get repetitive, especially if you have similar logic in multiple components. This is where custom hooks come in handy. They allow you to bundle reusable logic into a single function, making your code cleaner and easier to manage.

In this blog, we’ll create a custom hook called useCountrythat fetches information about countries from an API. We’ll break down each step to ensure you can easily follow along.

2. What is a React Hook?

Before we jump into custom hooks, let’s quickly review some built-in hooks in React:

useState: Think of this as a container that holds information that can change over time, like user input in a form.
useEffect: This is like a timer that tells React to perform a certain action after something happens, such as fetching data from a server.

A Quick Example

Imagine you’re building a simple form. You’d use useStateto keep track of the input values (like a shopping list) and useEffectto trigger actions when those values change (like updating the list).

These built-in hooks are the building blocks for creating custom hooks, which let you reuse logic across different components.

3. Step-by-Step Guide to Building the Custom Hook

Step 1: Define the Purpose of Your Hook

We’re going to create a custom hook called useCountry, which will fetch country details based on the name you provide. It will handle loading states and any errors that may occur.

Challenge: Think of another use case for a custom hook. What kind of data would you like to fetch?

Step 2: Initial Setup

We’ll use **Axios **to make our API calls. Axios simplifies the process of sending requests and gives better error messages compared to the Fetch API.

Here’s a basic outline of our custom hook:

import { useState, useEffect } from 'react';
import axios from 'axios';

function useCountry(countryName) {
  const [data, setData] = useState(null); // Holds fetched country data
  const [loading, setLoading] = useState(true); // Tracks loading state
  const [error, setError] = useState(null); // Tracks errors

  useEffect(() => {
    if (!countryName) return; // If countryName is empty, exit early

    const fetchCountry = async () => {
      setLoading(true); // Start loading
      try {
        const response = await axios.get(`https://restcountries.com/v3.1/name/${countryName}`);
        setData(response.data[0]); // Set country data
      } catch (err) {
        setError(err.response ? err.response.data.message : err.message); // Handle errors
      } finally {
        setLoading(false); // End loading
      }
    };

    fetchCountry(); // Call the function to fetch data
  }, [countryName]); // Re-run if countryName changes

  return { data, loading, error }; // Return state
}

export default useCountry;
Enter fullscreen mode Exit fullscreen mode

Key points:

  1. Axios automatically converts JSON responses, so you don’t have to do it manually.
  2. It provides better error messages, making debugging easier.

Try It Yourself: Copy the code into your editor and change the country name to see different results!

Step 3: Handle Edge Cases

It’s essential to manage edge cases — like when the country name is invalid or empty. Here’s how we can enhance our hook:

useEffect(() => {
  if (!countryName) {
    setError('Country name is required'); // Set error if no input
    setLoading(false);
    return; // Exit if country name is empty
  }

  const fetchCountry = async () => {
    setLoading(true);
    try {
      const response = await axios.get(`https://restcountries.com/v3.1/name/${countryName}`);
      setData(response.data[0]);
    } catch (err) {
      setError(err.response ? err.response.data.message : err.message);
    } finally {
      setLoading(false);
    }
  };

  fetchCountry();
}, [countryName]);
Enter fullscreen mode Exit fullscreen mode

Example Scenarios:

Invalid Country Name: If you input “Atlantis,” the API will return a 404 error, which we can catch and display.
Empty Input: If the input is empty, we show an error message indicating that a country name is required.

Step 4: Using the Custom Hook in a Component

Now that we have our useCountry hook, let’s use it in a component to fetch and show country details.

import React, { useState } from 'react';
import useCountry from './useCountry';

const CountryInfo = () => {
  const [country, setCountry] = useState(''); // State for country input
  const { data, loading, error } = useCountry(country); // Use custom hook

  const handleInputChange = (e) => {
    setCountry(e.target.value); // Update country state
  };

  return (
    <div>
      <input
        type="text"
        value={country}
        onChange={handleInputChange}
        placeholder="Enter country name"
      />
      {loading && <p>Loading...</p>} // Show loading message
      {error && <p>Error: {error}</p>} // Show error message
      {data && (
        <div>
          <h2>{data.name.common}</h2>
          <p>Population: {data.population}</p>
          <p>Region: {data.region}</p>
        </div>
      )}
    </div>
  );
};

export default CountryInfo;
Enter fullscreen mode Exit fullscreen mode

User Interaction

When a user types “Pakistan” into the input field, our component will fetch and display the country details.

Try It Yourself: Create an input field using the CountryInfocomponent. What happens when you enter different country names?

4. Best Practices for Custom Hooks

Naming Conventions: Always start your custom hook with “use,” like useCountry. This helps clarify that it follows React's hook rules.
Reusability: Make sure your hook is versatile enough to be used in multiple components. For example, a hook for form handling can work with different forms.
Handling Side Effects: Always use useEffectfor side effects (like API calls) within your hook. This keeps your components focused on their primary job.
Example: How could you modify useCountryto include a search history? Share your ideas!

5. When Not to Use Custom Hooks

Custom hooks are great, but they’re not always needed. Here are times when they might not be the best choice:

Single-Use Logic: If the logic is specific to one component and won’t be reused, there’s no need to create a custom hook.
Simple State Management: If managing state is straightforward (like a toggle switch), making a custom hook could complicate things.

Reflection: Think about recent projects. Have you created any logic that might benefit from being turned into a custom hook? Share your thoughts!

6. Conclusion

Custom hooks in React help you manage state and side effects while keeping your components clean and reusable. We’ve shown how to build a custom hook using Axiosto fetch country details, manage loading and error states, and handle edge cases.

By using custom hooks, you can make your code more organized and maintainable. Start creating your own custom hooks for tasks like data fetching or form handling!

Final Challenge

Try creating your own custom hook for a different purpose, such as fetching weather data. Can you build it? If not, feel free to revisit this material for a deeper understanding.

Top comments (0)