Custom Hooks: Your Secret Weapon in React
Custom hooks are like toolboxes for React developers. They let you package complex logic into reusable functions that any component can use—imagine turning a messy kitchen drawer into a neatly labeled toolkit. Let’s break down what they are, why they matter, and how to build them.
Why Custom Hooks?
-
Kill Repetition
If you’ve copied the same logic across multiple components (like fetching data or tracking window size), custom hooks eliminate the duplication. -
Organize Chaos
They help you separate concerns. Instead of a giant component that does everything, split logic into focused hooks. -
Reusability
Share hooks across projects or teams. Write once, use everywhere!
Example Without Custom Hooks: The Problem
function ComponentA() {
const [count, setCount] = useState(0);
// Duplicated counter logic
const increment = () => setCount(c => c + 1);
return <button onClick={increment}>{count}</button>;
}
function ComponentB() {
const [count, setCount] = useState(0);
// Same logic again!
const increment = () => setCount(c => c + 1);
return <div onClick={increment}>Count: {count}</div>;
}
Both components repeat the same counter logic. Yuck!
Building Your First Custom Hook
Let’s create useWindowSize to track browser window dimensions.
Step 1: Create the Hook
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);
return () => window.removeEventListener('resize', handleResize);
}, []); // Empty dependency = runs once
return windowSize;
}What’s Happening Here?
useStatetracks the window size.useEffectadds a resize listener and cleans it up (prevents memory leaks).- The hook returns the current window size.
Step 2: Use the Hook in Components
function ResponsiveComponent() {
const { width, height } = useWindowSize(); // Magic here!
return (
<div>
Window: {width}px x {height}px
</div>
);
}No duplicate resize logic! Just import and use.
Another Example: useFetch for Data Loading
Let’s build a hook to fetch data from an API.
The Hook
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(url)
.then((res) => res.json())
.then((data) => {
setData(data);
setLoading(false);
})
.catch((err) => {
setError(err);
setLoading(false);
});
}, [url]); // Re-runs if URL changes
return { data, loading, error };
}Using the Hook
function UserProfile() {
const { data, loading, error } = useFetch('/api/user/123');
if (loading) return <p>Loading...</p>;
if (error) return <p>Oops! {error.message}</p>;
return (
<div>
<h1>{data.name}</h1>
<p>{data.email}</p>
</div>
);
}Best Practices for Custom Hooks
-
Always Name Hooks with
use*This convention helps React (and other devs) recognize its a hook. -
Keep Hooks Focused Each hook should do one thing. Split complex logic into multiple hooks.
-
Leverage Dependency Arrays In
useEffect, specify when the effect should re-run (e.g., when a URL changes).
Conclusion
Custom hooks turn you into a React superhero:
- 🛠 Reuse logic effortlessly.
- 🧩 Simplify components by moving logic out.
- ⚡ Boost productivity across projects.
Remember: Great developers don’t repeat themselves—they build hooks!