React Custom Hooks: Reuse Logic and Simplify Complex Components

Last updated 2 months, 2 weeks ago | 146 views 75     5

Tags:- React

Introduction: Why React Custom Hooks Matter

React provides built-in hooks like useState, useEffect, and useContext to manage state and side effects. But when your components start growing in size and logic becomes repetitive—copying and pasting useEffect blocks all over the app is not scalable.

That’s where custom hooks come in.

React Custom Hooks let you:

  • Extract reusable logic from components.

  • Keep components clean and focused on rendering.

  • Encapsulate and test side effects or shared functionality more easily.

If you find yourself writing similar useState and useEffect code in multiple components, you probably need a custom hook.


What Are Custom Hooks in React?

A custom hook is a JavaScript function that:

  • Starts with the word use

  • Can call other hooks inside it (like useState, useEffect, useReducer, etc.)

  • Returns stateful logic or behavior

Syntax

function useCustomHook() {
  // useState, useEffect, etc.
  return value;
}

Step-by-Step Guide to Creating a Custom Hook

1. Identify Repetitive Logic

Example scenario: multiple components track window width using the same logic.

useEffect(() => {
  const handleResize = () => setWidth(window.innerWidth);
  window.addEventListener('resize', handleResize);
  return () => window.removeEventListener('resize', handleResize);
}, []);

Instead of duplicating this in every component, extract it into a custom hook.


2. Create the Custom Hook

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

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);

    // Cleanup
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return width;
}

export default useWindowWidth;

3. Use It in Your Components

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

function ResponsiveComponent() {
  const width = useWindowWidth();

  return (
    <div>
      <h2>Window width: {width}px</h2>
      {width < 600 ? <p>Mobile View</p> : <p>Desktop View</p>}
    </div>
  );
}

You’ve now abstracted logic and made it reusable and cleaner!


Complete Example: Custom Hook for Fetching Data

// 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;

    fetch(url)
      .then((res) => {
        if (!res.ok) throw new Error('Network response was not ok');
        return res.json();
      })
      .then((data) => {
        if (isMounted) {
          setData(data);
          setLoading(false);
        }
      })
      .catch((err) => {
        if (isMounted) {
          setError(err.message);
          setLoading(false);
        }
      });

    return () => {
      isMounted = false;
    };
  }, [url]);

  return { data, loading, error };
}

export default useFetch;
// App.js
import React from 'react';
import useFetch from './useFetch';

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

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

  return (
    <div>
      <h2>{data.title}</h2>
      <p>{data.body}</p>
    </div>
  );
}

export default App;

Tips & Common Pitfalls

Best Practices

  • Name hooks with use prefix so React knows how to handle them.

  • Keep hooks focused—each should do one thing (e.g., useWindowWidth, useLocalStorage).

  • Reuse hooks across multiple components to keep DRY.

⚠️ Common Pitfalls

  • Calling hooks conditionally inside your custom hook (bad pattern).

  • Returning JSX from custom hooks (they should only return logic or state).

  • Forgetting to return values needed by the consuming component.

  • Ignoring cleanup in useEffect—can lead to memory leaks.


Comparison Table: Built-in vs Custom Hooks

Feature Built-in Hooks (useState, useEffect, etc.) Custom Hooks
Provided by React ✅ Yes ❌ No (user-defined)
Reusable ⚠️ With boilerplate ✅ Highly reusable
Returns State, updater, effects Anything: state, functions, etc
Typical use cases State, side effects, context Encapsulated logic reuse

Use Cases for Custom Hooks

  • Fetching data from APIs

  • Debouncing input

  • Listening to window events

  • Managing form state

  • Persisting data in localStorage


Conclusion: Level Up with React Custom Hooks

Custom hooks are one of the most powerful features in modern React. They promote cleaner code, better separation of concerns, and reusable logic without reaching for external state management libraries.

If you find yourself copying and pasting stateful logic, it’s a signal to extract a custom hook.


Actionable Takeaways

  • Use custom hooks to encapsulate logic and reduce repetition.

  • Keep each custom hook focused on one job.

  • Don’t return JSX—return logic, data, or functions.

  • Use useEffect, useState, and other hooks inside your custom hook safely.