Introduction
A custom hook is simply a JavaScript function whose name starts with 'use' and that calls other hooks inside it. Custom hooks let you extract stateful logic — such as fetching data, subscribing to a browser event, or managing a form field — out of a component and into a reusable function that any component can call. Unlike regular helper functions, custom hooks can use useState, useEffect, useContext, and other hooks internally, and each component that calls a custom hook gets its own independent state.
Cricket analogy: Think of a custom hook like a fielding drill a coach designs, such as useSlipCatching, that any player can run through — Kohli and Rahane each practice it separately, keeping their own reps count and form, not a shared tally.
Syntax
import { useState, useEffect } from 'react';
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
function handleResize() {
setWidth(window.innerWidth);
}
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return width;
}Explanation
A custom hook is created by writing a function named useSomething and moving hook calls (useState, useEffect, useContext, useRef, etc.) into it, then returning whatever the calling component needs — a value, a tuple of value and setter, or an object of related values and functions. Because it follows the naming convention 'use*', React's linter can verify it obeys the Rules of Hooks, and it can itself call other hooks. Crucially, calling a custom hook does not share state between components: each call site gets its own isolated useState/useEffect instances, just as if the logic had been written inline in that component. This makes custom hooks a mechanism for reusing logic, not for reusing state across components — that's what Context or a shared store is for. Good candidates for extraction are logic patterns repeated across multiple components, such as data fetching with loading/error states, subscribing to a browser API, debouncing a value, or managing a toggle.
Cricket analogy: Building useStrikeRotation bundles useState for runs and useEffect for over-tracking into one function named with 'use' so tooling flags misuse, returning strike-rate data — Dhoni's innings and Kohli's innings each stay untouched and separate.
Example
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
setLoading(true);
fetch(url)
.then((res) => res.json())
.then((json) => {
if (!cancelled) {
setData(json);
setLoading(false);
}
})
.catch((err) => {
if (!cancelled) {
setError(err);
setLoading(false);
}
});
return () => {
cancelled = true;
};
}, [url]);
return { data, loading, error };
}
function UserProfile({ userId }) {
const { data, loading, error } = useFetch(`/api/users/${userId}`);
if (loading) return <p>Loading...</p>;
if (error) return <p>Something went wrong.</p>;
return <h2>{data.name}</h2>;
}Output
UserProfile shows 'Loading...' while the fetch request for the given userId is in flight, then renders the user's name once data arrives, or an error message if the request fails. If two different components both call useFetch with different URLs, each gets its own independent data, loading, and error state — the internal useState calls are not shared, only the logic pattern is reused.
Cricket analogy: Like a live scoreboard app showing 'Loading...' while fetching a player's stats, then displaying Kohli's average or an error if the API fails; a second widget fetching Bumrah's bowling stats via the same useFetch keeps its own separate loading and error flags.
Key Takeaways
- A custom hook is a function starting with 'use' that calls other hooks internally to encapsulate reusable stateful logic.
- Custom hooks must follow the Rules of Hooks, including being called at the top level, not inside loops or conditions.
- Each component using a custom hook gets independent state; custom hooks share logic, not state, across components.
- Good candidates for custom hooks include data fetching, event listeners, debouncing, and form field management.
- Custom hooks compose naturally by calling useState, useEffect, useContext, useRef, or even other custom hooks inside them.
Practice what you learned
1. What naming convention must a custom hook follow?
2. If two separate components call the same custom hook, do they share the same state?
3. Which of these is NOT true about custom hooks?
4. Which scenario is a good candidate for extraction into a custom hook?
Was this page helpful?
You May Also Like
Rules of Hooks
Learn the two Rules of Hooks — only call at the top level, only call from React functions — and why they exist.
The useEffect Hook
Understand useEffect for side effects in function components, covering the dependency array, cleanup functions, and effect timing.
The useState Hook
Master useState, the Hook that adds local state to function components, including initial values, updater functions, and functional updates.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics