Let's be real. Have you ever copied and pasted the same chunk of code between two different React components?
Maybe it was code to:
Fetch data from an API.
Manage a form's input values.
Listen to keyboard presses.
Connect to a websocket.
If you have, you've felt the pain. It works, but it's messy. And if you find a bug in that code, you have to remember to fix it in every single place you pasted it. Yuck.
What if there was a way to write that logic once and use it everywhere? There is. It's called a Custom Hook.
What is a Custom Hook, Really? (No Jargon)
Imagine you have a favorite power tool, like a drill. You don't build a new drill from scratch every time you need to put up a shelf. You just grab your drill from the toolbox and use it.
A Custom Hook is your personal power tool for React.
It's not a new React feature you have to install. It's just a JavaScript function whose name starts with "use" that can use other hooks inside it.
That's it. You're just making a function.
Let's Build a Tool Together: useLocalStorage
A super common need is to save something to the user's browser (like their username) so it doesn't disappear when they refresh the page.
The Messy Way (Without a Custom Hook):
You'd copy this same useState and useEffect code into every component that needs it
// Inside ComponentOne.js and ComponentTwo.js and... 😩
function MyComponent() {
const [name, setName] = useState(() => {
// Get the saved name from the browser when the component starts
const savedName = localStorage.getItem('name');
return savedName ? JSON.parse(savedName) : '';
});
useEffect(() => {
// Save the name to the browser every time it changes
localStorage.setItem('name', JSON.stringify(name));
}, [name]);
return <input value={name} onChange={e => setName(e.target.value)} />;
}
The Clean Way (With a Custom Hook):
Let's build our useLocalStorage power tool.
Step 1: Make a new file: useLocalStorage.js
Step 2: Build your tool inside it.
// useLocalStorage.js
import { useState, useEffect } from 'react';
// 1. Our function name starts with 'use'. This makes it a Hook.
function useLocalStorage(key, initialValue) {
// 2. We can use other Hooks inside our custom Hook! This is the magic.
const [value, setValue] = useState(() => {
// Get the saved value from the browser when the hook starts
const savedValue = localStorage.getItem(key);
return savedValue ? JSON.parse(savedValue) : initialValue;
});
useEffect(() => {
// Save the value to the browser every time it changes
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
// 3. Return the value and the function to update it, just like useState does!
return [value, setValue];
}
export default useLocalStorage;
Step 3: Now, use your powerful new tool anywhere!
// Inside AnyComponent.js
import useLocalStorage from './useLocalStorage';
function AnyComponent() {
// Just ONE line! Look how clean it is! ✨
const [name, setName] = useLocalStorage('name', '');
return <input value={name} onChange={e => setName(e.target.value)} />;
}
What Just Happened?
We Wrote Logic Once: All the messy localStorage code is in one place.
1.We Made It Reusable: Any component in our app can now use this useLocalStorage hook.
2.We Made It Easy to Fix: If we find a bug, we fix it in one file (useLocalStorage.js), and it's automatically fixed in every component that uses it.
Your Turn: How to Start
1.Look for repetition. The next time you're about to copy code from one component to another... STOP.
2.Create a new file. Name it use[YourFeature].js (e.g., useApi.js, useToggle.js).
3.Move your repeated code into that function.
4.Return the values that your component needs (usually a value and a setter, just like useState).
Import and use it in your components. Feel like a wizard. 🧙♂️
Custom Hooks aren't about being a React genius. They're about being lazy in the smartest way possible: writing less code and making your life easier.
What's the first thing you'll turn into a custom hook? Share your idea below!
If you’d like to support my content, you can buy me a coffee here:
Buy Me a Coffee
Top comments (0)