DEV Community

Cover image for Building a Custom Hook in React for Reusable Logic
Ayush Kumar Vishwakarma
Ayush Kumar Vishwakarma

Posted on

Building a Custom Hook in React for Reusable Logic

React’s Hooks introduced a powerful way to manage state and lifecycle methods in functional components. One standout feature is the ability to create custom hooks, which allow developers to extract and reuse logic across components. Custom hooks improve code readability, maintainability, and scalability in React applications.

In this article, we’ll walk through what custom hooks are, why they are useful, and how to create one with a practical example.

What is a Custom Hook?
A custom hook is a JavaScript function that starts with the prefix use and allows you to encapsulate reusable logic in a declarative way. It enables you to combine multiple built-in hooks (like useState, useEffect, etc.) into a single function that can be shared across components.

Key Features:

  • Begins with use (e.g., useCustomHook).
  • Can utilize other React hooks.
  • Returns data, state, or functions needed by components.

Why Use Custom Hooks?

  • Reusability: Share logic between components without duplicating code.
  • Readability: Encapsulate complex logic, making components simpler and easier to read.
  • Separation of Concerns: Isolate logic into reusable functions, adhering to clean code principles.
  • Testability: Simplify testing by isolating logic from components.

Creating a Custom Hook: A Step-by-Step Guide
Let’s create a custom hook called useFetch to fetch data from an API. This hook will handle fetching, loading states, and error management.

1. Define the Custom Hook
Create a new file called 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(() => {
    let isMounted = true; // Prevent setting state on unmounted components
    const fetchData = async () => {
      setLoading(true);
      try {
        const response = await fetch(url);
        if (!response.ok) {
          throw new Error(`Error: ${response.status}`);
        }
        const result = await response.json();
        if (isMounted) {
          setData(result);
          setError(null);
        }
      } catch (err) {
        if (isMounted) {
          setError(err.message);
        }
      } finally {
        if (isMounted) {
          setLoading(false);
        }
      }
    };

    fetchData();

    // Cleanup to avoid memory leaks
    return () => {
      isMounted = false;
    };
  }, [url]);

  return { data, loading, error };
}

export default useFetch;
Enter fullscreen mode Exit fullscreen mode

2. Use the Custom Hook in a Component
Here’s how you can use useFetch in a component:

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

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

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

  return (
    <div>
      <h1>Posts</h1>
      <ul>
        {data.map((post) => (
          <li key={post.id}>
            <strong>{post.title}</strong>
            <p>{post.body}</p>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Anatomy of the Custom Hook

1. Input Parameters:

  • The url parameter allows you to specify the API endpoint dynamically.

2. State Management:

  • useState is used to manage the data, loading, and error states.

3. Effect Hook:

  • useEffect performs the side effect of fetching data and cleans up to prevent memory leaks.

4. Return Values:

  • The hook returns an object with data, loading, and error to provide all necessary information to the consuming component.

Real-World Use Cases for Custom Hooks

  1. Form Handling: Create a useForm hook to manage form state and validation.
  2. Authentication: Develop a useAuth hook for handling user authentication logic.
  3. Theme Management: Implement a useTheme hook for switching between light and dark modes.
  4. Pagination: Build a usePagination hook to manage paginated data in tables or lists.

Best Practices for Writing Custom Hooks

  1. Use Descriptive Names: Name your hook clearly to indicate its purpose (e.g., useFetch, useAuth).
  2. Follow the use Prefix Rule: Always start the function name with use to ensure React recognizes it as a hook.
  3. Keep it Focused: Write hooks that address a specific piece of functionality.
  4. Avoid Side Effects in Render: Ensure hooks do not perform operations that affect rendering, such as API calls without useEffect.

Conclusion
Custom hooks are a powerful feature of React that enable developers to encapsulate and reuse logic across components. By separating logic from UI, they improve code organization, readability, and reusability. Whether you're fetching data, managing forms, or handling authentication, custom hooks can make your React applications more efficient and maintainable.

Start by identifying repetitive logic in your components and extract it into custom hooks. The result will be cleaner, more scalable code that is easier to understand and maintain.

Top comments (0)