React Mastery Series – Day 18: Custom Hooks in React – Reusing Logic Like a Pro
Welcome back to the React Mastery Series! In the previous article, we explored useReducer and learned how it helps manage complex state transitions in a predictable way. As React applications grow, another challenge em
Welcome back to the React Mastery Series!
In the previous article, we explored useReducer and learned how it helps manage complex state transitions in a predictable way.
As React applications grow, another challenge emerges:
How do we avoid repeating the same logic across multiple components?
Imagine implementing:
- API calls
- Authentication
- Form validation
- Window resize detection
- Debouncing search input
- Online/offline status monitoring
If every component contains the same logic, the application quickly becomes difficult to maintain.
React solves this elegantly using:
Custom Hooks
Custom Hooks allow you to extract reusable stateful logic into reusable functions.
What is a Custom Hook?
A Custom Hook is simply a JavaScript function that:
- Starts with the word
use - Can use other React Hooks
- Encapsulates reusable logic
- Can be shared across multiple components
Example:
function useCounter() {
const [count, setCount] = useState(0);
const increment = () => setCount(c => c + 1);
return {
count,
increment
};
}
Now any component can use it like a built-in Hook.
const { count, increment } = useCounter();
Why Do We Need Custom Hooks?
Consider two components.
User Dashboard
useEffect(() => {
fetchUsers();
}, []);
Admin Dashboard
useEffect(() => {
fetchUsers();
}, []);
Both components contain identical logic.
This leads to:
- Duplicate code
- Difficult maintenance
- Higher chance of bugs
Instead, extract the logic into a Custom Hook.
Creating Your First Custom Hook
Example:
import { useState } from "react";
function useCounter() {
const [count, setCount] = useState(0);
const increment = () =>
setCount(c => c + 1);
const decrement = () =>
setCount(c => c - 1);
return {
count,
increment,
decrement
};
}
Using it:
function Counter() {
const {
count,
increment,
decrement
} = useCounter();
return (
<>
<h2>{count}</h2>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</>
);
}
The logic is reusable and isolated.
Real-World Example: Fetching Data
Many components fetch data from APIs.
Instead of repeating this logic, create a reusable Hook.
function useUsers() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/api/users")
.then(response => response.json())
.then(data => {
setUsers(data);
setLoading(false);
});
}, []);
return {
users,
loading
};
}
Now multiple components can simply do:
const {users, loading} = useUsers();
No duplicated fetching logic.
Example: Window Width Hook
Imagine several pages need the browser width.
Instead of adding event listeners everywhere:
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize",handleResize);
};
}, []);
return width;
}
Usage:
const width = useWindowWidth();
Every component now shares the same reusable logic.
Example: Online Status Hook
Modern applications often need to know if the user is online.
function useOnlineStatus() {
const [online, setOnline] = useState(navigator.onLine);
useEffect(() => {
const goOnline = () => setOnline(true);
const goOffline = () => setOnline(false);
window.addEventListener("online",goOnline);
window.addEventListener("offline",goOffline);
return () => {
window.removeEventListener("online",goOnline);
window.removeEventListener("offline",goOffline);
};
}, []);
return online;
}
Usage:
const isOnline = useOnlineStatus();
Perfect for:
- Banking apps
- Chat applications
- Video conferencing
- Trading platforms
Custom Hooks Can Use Other Hooks
A common misconception is that Custom Hooks are different from React Hooks.
In reality, they are built using existing Hooks.
Example:
Custom Hook
|
├── useState
├── useEffect
├── useContext
├── useReducer
└── useRef
You can combine multiple Hooks inside one Custom Hook.
Sharing Logic, Not State
A very important concept:
Each component gets its own independent state.
Example:
const counter1 = useCounter();
const counter2 = useCounter();
Result:
Counter 1 → 5
Counter 2 → 12
Although the logic is shared, the state is not.
Each Hook instance is isolated.
Real-World Enterprise Example
Imagine a retail banking application.
Several pages need customer information.
Instead of writing:
Dashboard
↓
API Call
↓
Profile
↓
API Call
↓
Settings
↓
API Call
Create:
const { customer, loading } = useCustomer();
Now every page uses the same business logic.
Benefits:
- Less code
- Easier maintenance
- Consistent behavior
- Centralized API logic
Organizing Custom Hooks
A common project structure:
src
|
├── hooks
| |
| ├── useAuth.js
| ├── useFetch.js
| ├── useWindowWidth.js
| ├── useTheme.js
| └── useDebounce.js
|
├── components
|
└── pages
Keeping Hooks in a dedicated folder improves project organization.
Common Custom Hooks
Many React projects contain Hooks like:
useAuth()useTheme()useFetch()useLocalStorage()useDebounce()usePrevious()useOnlineStatus()usePagination()
These Hooks encapsulate commonly used logic.
Common Mistakes
1. Not Prefixing with "use"
Incorrect:
function counter() {}
Correct:
function useCounter() {}
React relies on this naming convention to enforce the Rules of Hooks.
2. Calling Hooks Conditionally
Incorrect:
if (loggedIn) {
useAuth();
}
Hooks must always be called at the top level.
Correct:
const auth = useAuth();
if (loggedIn) {
// Use auth here
}
3. Putting UI Inside a Hook
Incorrect:
function useGreeting() {
return <h1>Hello</h1>;
}
Hooks should return:
- State
- Values
- Functions
They should not return JSX.
Best Practices
- Name every Custom Hook with the
useprefix. - Keep Hooks focused on one responsibility.
- Extract repeated logic into Hooks.
- Return only what consumers need.
- Keep UI rendering inside components.
- Document Hook inputs and outputs for team projects.
Key Takeaways
Today, we learned:
✅ Custom Hooks let you reuse stateful logic across components.
✅ A Custom Hook is simply a function that uses React Hooks.
✅ They reduce code duplication and improve maintainability.
✅ Custom Hooks share logic, not state.
✅ Organizing reusable Hooks makes large applications easier to manage.
Coming Next 🚀
In Day 19, we will explore:
Routing in React – Building Single Page Applications with React Router
We will learn:
- What routing is
- Why Single Page Applications need routing
- Installing React Router
- Creating routes
- Route parameters
- Nested routes
- Protected routes
- Navigation with
LinkanduseNavigate - Real-world authentication routing
Routing is a fundamental skill for every React developer and is used in virtually every production React application.
Happy Coding! 🚀
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.