Hey everyone! 👋
Today we’re tackling a common habit that even I’m guilty of: Copy-Pasting Code.
You write a cool piece of logic to fetch data in one component. Then you need it in another component. Ctrl+C, Ctrl+V. Done, right?
Well, until you need to fix a bug in that logic. Now you have to fix it in 10 different places. 😫
Enter Custom Hooks.
This is the secret weapon to keeping your React components clean, readable, and—most importantly—DRY (Don’t Repeat Yourself).
The Problem: Repetitive Logic
Let’s say we have two components. Both need to track the window size (maybe for a responsive layout).
Here is Component A:
import { useState, useEffect } from "react";
const ComponentA = () => {
const [windowSize, setWindowSize] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWindowSize(window.innerWidth);
window.addEventListener("resize", handleResize);
// Cleanup
return () => window.removeEventListener("resize", handleResize);
}, []);
return <div>Window width is: {windowSize}</div>;
};
Now imagine Component B needs the exact same logic. You’d have to duplicate all that useState and useEffect code.
That’s a lot of boilerplate for just one number!
The Solution: Custom Hooks
A Custom Hook is just a JavaScript function. The only rule? It must start with “use”.
Let’s extract that window resizing logic into useWindowSize.
// hooks/useWindowSize.js
import { useState, useEffect } from "react";
export const useWindowSize = () => {
// 1. We just move the logic here!
const [windowSize, setWindowSize] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWindowSize(window.innerWidth);
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
// 2. Return whatever the component needs
return windowSize;
};
That’s it. Really. It’s just a function that calls other hooks.
The Result: Clean Components ✨
Now, let’s rewrite Component A using our new superpower.
import { useWindowSize } from "./hooks/useWindowSize";
const ComponentA = () => {
// Look how clean this is!
const width = useWindowSize();
return <div>Window width is: {width}</div>;
};
And Component B?
import { useWindowSize } from "./hooks/useWindowSize";
const ComponentB = () => {
const width = useWindowSize();
return (
<div style={{ backgroundColor: width > 768 ? "green" : "red" }}>
Responsive Background!
</div>
);
};
Why is this awesome?
- Readability: Your component focuses on rendering, not logic.
- Reusability: Write once, use everywhere.
- Maintainability: If you need to change how the resizing works (e.g., adding a debounce), you change it in ONE place.
Closing
Custom Hooks aren’t some advanced, scary feature. They are just a way to organize your code better.
Next time you find yourself copying useEffect or useState logic, pause and ask:
“Can I make this a hook?”
Your future self (and your teammates) will thank you.
Happy Coding! 🚀