Your Logic, Your Rules: Custom React Hooks

Extract your component logic into reusable functions to build cleaner, more scalable React applications.

Lesson ProgressStep 1 of 8

UserProfile.js

Fetches user data...

ProductPage.js

Fetches product data...

useFetch.js

Reusable Logic 📦

0 EXP

Hello! Let's see how to make our code much cleaner. This component fetches user data...

function UserProfile() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  useEffect(() => { /* fetch logic... */ }, []);
  /* ... */
}

What is a Custom Hook?

A **Custom Hook** is a JavaScript function whose name starts with `use` and that can call other Hooks (like `useState` or `useEffect`).

Its purpose is to extract component logic into reusable functions. If you find yourself writing the same stateful logic in multiple components (like fetching data, managing a form, or connecting to a subscription), you can extract that logic into a custom hook.

// This is NOT a hook
function getWindowWidth() { /* ... */ }

// This IS a hook (starts with 'use')
function useWindowWidth() { /* ... */ }

System Check

What is the primary convention for naming a custom hook?

Advanced Holo-Simulations

0 EXP

Log in to unlock these advanced training modules and test your skills.


Achievements

🏆
Hook Builder

Construct your first well-formed custom hook.

🏗️
Logic Extractor

Correctly identify and extract reusable logic from components.

✍️
Rules Master

Prove your mastery of the Rules of Hooks.

Mission: Build a `useToggle` Hook

Create a custom hook named `useToggle` that takes an `initialValue` (boolean). It should return an array with the current value and a function to toggle that value.

A.D.A. Feedback:

> Awaiting input...

Challenge: Order the Hook Logic

Drag the pieces of a custom hook into the correct logical order.

const [state, setState] = useState(...)
return { state, updateFunction }
function useMyHook(initialValue) {
useEffect(() => { ... }, [dependencies])

Challenge: Complete the Syntax

Fill in the missing React hooks to build a `useDocumentTitle` hook.

function useDocumentTitle(title) {
const [docTitle, setDocTitle] =(title);
(() => { document.title = docTitle; }, [docTitle]);
[docTitle, setDocTitle];
}

Consult A.D.A.

Community Holo-Net

Your Logic, Your Rules: The Power of Custom Hooks

In React, a **Custom Hook** is more than just a function. It's a convention that allows you to extract and reuse **stateful logic** from a component. If you find yourself copying and pasting the same `useState` and `useEffect` logic across multiple components, you've found a perfect candidate for a custom hook.

This pattern is the cornerstone of a clean, scalable, and maintainable React codebase. It follows the **DRY (Don't Repeat Yourself)** principle, allowing you to build complex UIs from simple, declarative, and testable building blocks.

The Two Rules You Must Follow

React relies on two simple rules to make Hooks work. Violating them will lead to confusing bugs and unexpected behavior.

  • 1. Only Call Hooks at the Top Level: Do not call Hooks inside loops, conditions, or nested functions. This ensures that Hooks are called in the same order on every single render, which is how React preserves state between calls.
  • 2. Only Call Hooks from React Functions: You can only call Hooks from a React functional component or from another custom hook. You cannot call them from regular JavaScript functions.

By convention, custom hooks must also have a name that starts with `use` (e.g., `useFetch`, `useToggle`). This allows React and linters to automatically check for violations of the Rules of Hooks.

Pattern 1: The `useToggle` Hook (Simple State)

The simplest custom hook often just abstracts a single `useState` call and its update logic. A common example is a boolean toggle.

import { useState, useCallback } from 'react';

function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue);
  
  // useCallback ensures the toggle function has a stable identity
  const toggle = useCallback(() => {
    setValue(prev => !prev);
  }, []);
  
  return [value, toggle];
}

// How to use it:
// const [isOn, toggleIsOn] = useToggle(false);

This hook encapsulates the logic of "toggling a boolean." The component using it doesn't need to know *how* the toggle works; it just receives the current state (`value`) and the function to change it (`toggle`).

Pattern 2: The `useFetch` Hook (State + Effect)

A more powerful pattern involves combining `useState` with `useEffect` to manage side effects, like fetching data from an API.

import { useState, useEffect } from 'react';

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

  useEffect(() => {
    // AbortController handles component unmounting mid-fetch
    const controller = new AbortController();
    
    setLoading(true);
    fetch(url, { signal: controller.signal })
      .then(res => {
        if (!res.ok) throw new Error(res.statusText);
        return res.json();
      })
      .then(data => setData(data))
      .catch(err => {
        if (err.name !== 'AbortError') {
          setError(err);
        }
      })
      .finally(() => setLoading(false));
      
    // Cleanup function: Abort fetch if component unmounts
    return () => controller.abort();
    
  }, [url]); // Re-run the effect if the URL changes

  return { data, loading, error };
}

This hook is a complete data-fetching solution. It handles loading state, error state, and even cleans up after itself to prevent memory leaks. Any component can now fetch data with a single line: const { data, loading, error } = useFetch('/api/my-data');

Pattern 3: The `useWindowSize` Hook (Effect with Cleanup)

Custom hooks are perfect for abstracting browser APIs. This hook listens to the window's `resize` event and provides the current dimensions.

import { useState, useEffect } from 'react';

function useWindowSize() {
  const [size, setSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight,
  });

  useEffect(() => {
    const handleResize = () => {
      setSize({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    };
    
    window.addEventListener('resize', handleResize);
    
    // Crucial cleanup: remove the listener when the hook unmounts
    return () => window.removeEventListener('resize', handleResize);
  }, []); // Empty array means this effect runs only once (on mount)

  return size;
}

// How to use it:
// const { width, height } = useWindowSize();

The most important part of this hook is the **cleanup function** returned from `useEffect`. Without it, you would add a new event listener every time the component mounts, creating a massive memory leak.

Key Takeaway: Custom Hooks transform your components from complex, state-managing monoliths into clean, declarative functions that simply consume logic. This makes your code easier to read, test, and maintain.

React Hooks & Patterns Glossary

Custom Hook
A JavaScript function whose name starts with `use` and that can call other React Hooks. It's a convention for reusing stateful logic between components.
Stateful Logic
Any logic that involves managing state (`useState`), side effects (`useEffect`), context (`useContext`), or other React hooks. This is what custom hooks are designed to extract and share.
DRY (Don't Repeat Yourself)
A principle of software development aimed at reducing the repetition of logic. Custom hooks are the primary tool in React for adhering to this principle.
Rules of Hooks
The two critical rules: 1) Only call Hooks at the top level of a React function (not in loops, conditions, or nested functions). 2) Only call Hooks from React functional components or other custom hooks.
Side Effect
Any operation that affects something outside of the component's render scope. Common examples include data fetching, setting up a subscription, or manually changing the DOM. Handled by the `useEffect` hook.
Cleanup Function
A function that can be optionally returned from a `useEffect` hook. React runs this function when the component unmounts, or before the effect runs again. It's used to "clean up" side effects, like canceling subscriptions or removing event listeners.
Dependency Array
The second argument to `useEffect`, `useCallback`, and `useMemo`. It's an array of values that the hook "depends on." The hook will only re-run its function if one of these values changes between renders. An empty array `[]` means the effect runs only once on mount.
`useState`
A built-in hook that lets you add React state to a functional component. It returns an array with the current state value and a function to update it.
`useEffect`
A built-in hook that lets you perform side effects in functional components. It takes a function to run and a dependency array.
`useCallback`
A built-in hook that returns a "memoized" version of a callback function. The function reference will only change if a value in its dependency array changes. This is used to optimize performance by preventing unnecessary re-renders of child components.

About the Author

Author's Avatar

TodoTutorial Team

Passionate developers and educators making programming accessible to everyone.

This article was written and reviewed by our team of web development experts, who have years of experience teaching React and building robust, scalable web applications.

Verification and Updates

Last reviewed: October 2025.

We strive to keep our content accurate and up-to-date. This tutorial is based on the latest React v18+ specifications and is periodically reviewed to reflect industry best practices.

External Resources

Found an error or have a suggestion? Contact us!