Skip to main content
React Introduction
CHAPTER 22 Beginner

React Custom Hooks

Updated: May 13, 2026
25 min read

# React Custom Hooks

1. Introduction

As your applications grow, you will find yourself writing the exact same useState and useEffect logic in multiple components. For example, fetching data from an API usually requires loading, error, and data states. Repeating this in 10 different files is bad practice. To solve this, React allows you to create Custom Hooks: reusable JavaScript functions that encapsulate stateful logic.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Identify repeated logic that can be extracted into a Custom Hook.
  • Follow the naming conventions for Custom Hooks.
  • Create a reusable data-fetching hook.
  • Understand how custom hooks share logic, not state.

3. Beginner-Friendly Explanations

What is a Custom Hook?

A custom hook is simply a regular JavaScript function. The only difference is:
  1. 1. Its name must start with use (e.g., useFetch, useWindowSize, useAuth).
  1. 2. It can call other React Hooks (like useState and useEffect) inside of it.

If you extract UI into a reusable Component, you extract Logic into a reusable Custom Hook.

Sharing Logic, NOT State

If Component A and Component B both use a custom useCounter hook, they do not share the same count. They each get a completely independent copy of the state. Custom hooks reuse the *behavior*, not the *data*.

4. Syntax Explanation

Step 1: Identifying Repeated Logic

Imagine we need to track the window width in multiple components.

Without Custom Hook (Messy): ```jsx id="ch22_ex1" function ComponentA() { const [width, setWidth] = useState(window.innerWidth); useEffect(() => { const handleResize = () => setWidth(window.innerWidth); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); return <div>Width is {width}</div>; }

123
### Step 2: Creating the Custom Hook
We extract that exact logic into a new function starting with `use`.

jsx id="ch22_ex2" // useWindowWidth.js import { useState, useEffect } from 'react';

// 1. Name starts with 'use' export function useWindowWidth() { const [width, setWidth] = useState(window.innerWidth);

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

// 2. Return the data you want the components to access return width; }

123
### Step 3: Using the Custom Hook
Now, our components are incredibly clean!

jsx id="ch22_ex3" // ComponentA.jsx import { useWindowWidth } from './useWindowWidth';

function ComponentA() { const width = useWindowWidth(); // 1 line of code! return <div>Width is {width}</div>; }

1234567891011121314151617181920212223242526
## 5. Output Explanations
When `ComponentA` runs, it calls `useWindowWidth()`. React treats the `useState` and `useEffect` inside the custom hook exactly as if they were written directly inside `ComponentA`. 

## 6. Real-World Examples
Custom hooks are the secret weapon of senior developers.
- `useFetch(url)`: Handles API requests, loading, and error states.
- `useForm(initialValues)`: Handles form state and validation logic.
- `useLocalStorage(key, initialValue)`: Syncs state with the browser's local storage.
- `useKeyPress(targetKey)`: Detects if a specific keyboard key is pressed.

## 7. Common Mistakes
- **Forgetting the `use` prefix:** If you name your function `getWindowWidth`, React will throw an error when you try to call `useState` inside it. React's linter relies strictly on the `use` prefix to know it is a hook.
- **Calling hooks inside loops/conditions:** Custom hooks follow the exact same rules as built-in hooks. They must be called at the top level of your component.

## 8. Best Practices
- If your custom hook returns multiple values (like data, loading, error), return them as an object or an array so the consuming component can destructure them cleanly.
  - Array pattern: `return [data, loading]` => `const [data, loading] = useHook()`
  - Object pattern: `return { data, loading }` => `const { data } = useHook()` (Good if you don't need all the values).

## 9. Exercises
1. Create a `useToggle` hook. It should hold a boolean state, and return an array `[value, toggleFunction]`.

## 10. Mini Project: Reusable Data Fetcher
Let's build the most famous custom hook: `useFetch`. This single hook will save you hundreds of lines of code across a large application.

**1. The Hook (useFetch.js)**

jsx id="ch22proj1a" // Example React Custom Hook import { useState, useEffect } from 'react';

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

useEffect(() => { const fetchData = async () => { setLoading(true); try { const response = await fetch(url); if (!response.ok) throw new Error(HTTP error! status: ${response.status}); const json = await response.json(); setData(json); setError(null); } catch (e) { setError(e.message); } finally { setLoading(false); } };

fetchData(); }, [url]); // Re-run if the URL changes

// Return as an object for easy destructuring return { data, loading, error }; }

1
**2. Consuming the Hook (App.jsx)**

jsx id="ch22proj1b" // Example React component import React from 'react'; import { useFetch } from './useFetch';

export default function UserDashboard() { // Look how clean this is! const { data: users, loading, error } = useFetch('https://jsonplaceholder.typicode.com/users');

return ( <div className="min-h-screen bg-slate-50 p-8 font-sans"> <div className="max-w-2xl mx-auto bg-white p-8 rounded-2xl shadow border border-slate-100"> <h1 className="text-3xl font-black text-slate-800 mb-6">User Directory</h1>

{loading && ( <div className="flex justify-center my-10"> <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div> </div> )}

{error && ( <div className="bg-red-50 text-red-600 p-4 rounded-lg font-medium border border-red-200"> Error loading users: {error} </div> )}

{!loading && !error && users && ( <ul className="space-y-3"> {users.map(user => ( <li key={user.id} className="p-4 bg-slate-50 rounded-lg flex items-center justify-between border border-slate-200 hover:border-blue-300 transition-colors"> <span className="font-bold text-slate-700">{user.name}</span> <span className="text-sm text-slate-500 font-medium">{user.email}</span> </li> ))} </ul> )} </div> </div> ); } ``

11. Coding Challenges

Challenge 1: Create a
useLocalStorage(key, initialValue) hook. It should initialize state by reading from window.localStorage, and every time the state is updated via the setter, it should also save the new value back to localStorage.

12. MCQs with Answers

Q1: What is the strict naming requirement for Custom Hooks? A) They must end with "Hook". B) They must start with "use" (lowercase). C) They must be named in all uppercase. *Answer: B*

Q2: If two components use the same useCounter custom hook, do they share the same count state? A) Yes, custom hooks act as global state. B) No, each component gets a completely independent instance of the state. *Answer: B*

13. Interview Questions

  • Q: What is the primary purpose of writing a Custom Hook?
  • Q: Can a Custom Hook return JSX? *(Answer: No, if it returns JSX, it is a Component. Hooks return data or functions).*

14. FAQs

Are there libraries for Custom Hooks? Yes! Libraries like
react-use or usehooks-ts provide dozens of pre-written custom hooks (like useHover, useDebounce, useGeolocation) that you can install via npm.

15. Summary

Custom Hooks are the ultimate tool for logic reusability. By wrapping complex
useState and useEffect logic inside a function starting with use`, you can clean up your components, drastically reduce code duplication, and build a library of highly useful tools.

16. Next Chapter Recommendation

You now have components, contexts, and custom hooks. How do you organize all these files in a real-world application? In Chapter 23: React Project Structure Best Practices, we will explore enterprise-level folder architectures.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·