One of the most powerful features of React Hooks is the ability to create your own Custom Hooks. Custom Hooks allow you to extract component logic into reusable functions, keeping your components clean, concise, and focused strictly on the UI.
A Custom Hook is simply a JavaScript function whose name starts with "use" and that calls other React Hooks inside it.
If you find yourself writing the same useState and useEffect logic in multiple different components (like fetching data, tracking window size, or managing form inputs), that logic is a perfect candidate for a Custom Hook.
use: Your function must be named starting with use (e.g., useFetch, useWindowSize, useAuth). This is required! React's linter uses this naming convention to check for violations of the Rules of Hooks.useState or useEffect). Otherwise, it's just a regular JavaScript utility function.useFetchData fetching is one of the most common things developers do in React. Let's extract fetching logic into a Custom Hook so we don't have to write useEffect over and over again.
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(() => { // Reset states before fetching setLoading(true); fetch(url) .then((response) => { if (!response.ok) throw new Error("Network error"); return response.json(); }) .then((data) => { setData(data); setError(null); }) .catch((err) => { setError(err.message); setData(null); }) .finally(() => { setLoading(false); }); }, [url]); // Re-run if the URL changes
// Return the data as an object or array return { data, loading, error }; }
export default useFetch;
Now look at how clean our component becomes!
import React from 'react'; import useFetch from './useFetch';function UserList() { // Utilizing our custom hook! const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/users');
if (loading) return <p>Loading users...</p>; if (error) return <p>Error: {error}</p>;
return ( <ul> {data && data.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> ); }
useWindowSizeSometimes you need to know the width and height of the browser window to render responsive UI elements conditionally via JavaScript.
import { useState, useEffect } from 'react';function useWindowSize() { const [windowSize, setWindowSize] = useState({ width: window.innerWidth, height: window.innerHeight, });
useEffect(() => { function handleResize() { setWindowSize({ width: window.innerWidth, height: window.innerHeight, }); } window.addEventListener("resize", handleResize); // Cleanup the event listener on unmount return () => window.removeEventListener("resize", handleResize); }, []); // Empty array ensures listener is added only once
return windowSize; }
Any component can now simply call const { width } = useWindowSize() and it will dynamically update whenever the user resizes the browser window!
What is a mandatory rule when naming a Custom Hook in React?