Building a Custom Hook in React: A Step-by-Step Guide

Building a Custom Hook in React: A Step-by-Step Guide

React’s built-in hooks like useState and useEffect are powerful, but sometimes, you need reusable logic that can be shared across multiple components. That is where custom hooks come in.

In this guide, we will walk through creating a custom hook step by step, focusing on fetching data from an API using useState and useEffect.

What is a Custom Hook?

A custom hook in React is simply a function that starts with use and allows you to reuse stateful logic across components.

Why use custom hooks?

  • Improves code reusability

  • Reduces duplication

  • Keeps components clean and readable

Step 1: Setting Up the Project

To follow along, ensure you have a React project ready. If not, create one using:

npx create-react-app custom-hook-example
cd custom-hook-example
npm start

Step 2: Creating a Basic API Fetch Hook (useFetch)

Let’s create a custom hook that fetches data from an API.

Inside your src folder, create a new file: useFetch.js

The Custom Hook

import { useState, useEffect } from "react";

const 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("Failed to fetch data");
        }
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
};

export default useFetch;

Step 3: Using useFetch in a Component

Now, let’s use our custom hook inside a React component.

Open App.js and update it:

Using useFetch in App.js

import React from "react";
import useFetch from "./useFetch";

const App = () => {
  const { data, loading, error } = useFetch("https://jsonplaceholder.typicode.com/posts");

  if (loading) return <h2>Loading...</h2>;
  if (error) return <h2>Error: {error}</h2>;

  return (
    <div>
      <h1>Posts</h1>
      <ul>
        {data.slice(0, 5).map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
};

export default App;

Step 4: Explanation of the Hook

State Management

We use useState to manage data, loading, and error states.

Side Effects

We use useEffect to fetch data when the component mounts or when the url changes.

Reusability

This hook can be used in multiple components without rewriting the logic, making the code more modular and easier to maintain.

Step 5: Extending the Custom Hook

To make our hook more flexible, we can modify useFetch to accept an options parameter for fetch.

const useFetch = (url, options = {}) => {
  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url, options);
        if (!response.ok) {
          throw new Error("Failed to fetch data");
        }
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };
    fetchData();
  }, [url, options]);
};

Now, useFetch can send POST, PUT, or DELETE requests by passing different options.

Common Use Cases for Custom Hooks

  1. Fetching Data - The useFetch hook we built can be reused across multiple components to fetch data.

  2. Form Handling - Custom hooks can help manage form state and validation logic.

  3. Debouncing Input - Creating a useDebounce hook helps optimize performance when handling user input.

  4. Theme Management - A useTheme hook can be used to toggle between dark and light mode.

  5. Local Storage Handling - A useLocalStorage hook can be created to store and retrieve data from the browser's local storage.

Conclusion

Custom hooks help in creating more modular and maintainable React applications. By following this guide, you have learned how to:

  • Create a custom hook for fetching data.

  • Use it inside a React component.

  • Extend it to make it more flexible.

By implementing custom hooks, you can simplify your code, improve performance, and enhance reusability. Experiment with creating your own hooks for different functionalities in your applications.