Introduction
As React applications grow, developers often repeat the same logic in multiple components. Repeating code makes applications harder to maintain and increases development time. To solve this problem, React introduced Custom Hooks.
Custom Hooks help developers reuse component logic in a clean and organized way. They make code more readable, reusable, and easier to manage.
What are Custom Hooks in ReactJS?
A Custom Hook is a JavaScript function that starts with the word use and can call other React Hooks such as useState, useEffect, useContext, etc.
Custom Hooks are used to extract reusable logic from components.
Instead of writing the same code again and again, developers can create a custom hook and reuse it wherever needed.
Example
function useCounter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
return { count, increment };
}
The above function is a custom hook because:
- It starts with use
- It uses React Hook (useState)
- It contains reusable logic
Why Custom Hooks are Used?
Custom Hooks are mainly used for code reusability and better project structure.
Benefits of Custom Hooks
1. Reusable Logic
You can use the same functionality in multiple components.
2. Cleaner Code
Components become smaller and easier to understand.
3. Better Maintainability
Updating logic in one place automatically updates all components using that hook.
4. Separation of Concerns
UI logic and business logic remain separate.
5. Improved Readabilit
Developers can understand code more easily.
Syntax of Custom Hooks
The basic syntax of a custom hook is:
function useHookName() {
// Hook logic
return value;
}
Important Rules
Rule 1: Hook Name Must Start with use
Correct:
useCounter()
Wrong:
counterHook()
Rule 2: Only Call Hooks at Top Level
Do not use hooks inside loops, conditions, or nested functions.
Correct:
const [count, setCount] = useState(0);
Wrong:
if (true) {
useState(0);
}
Rule 3: Hooks Can Only Be Used Inside React Components or Custom Hooks
Basic Example of Custom Hook
Step 1: Create Custom Hook
useCounter.js
import React from "react";
import useFetch from "./useFetch";
function Users() {
const { data, loading, error } =
useFetch("https://jsonplaceholder.typicode.com/users");
if (loading) {
return <h2>Loading...</h2>;
}
if (error) {
return <h2>Error occurred</h2>;
}
return (
<div>
<h1>User List</h1>
{
data.map((user) => (
<p key={user.id}>
{user.name}
</p>
))}
</div>
);
}
export default Users;
Step 2: Use Custom Hook in Component
App.js
import React from "react";
import useCounter from "./useCounter";
function App() {
const { count, increment, decrement } = useCounter();
return (
<div>
<h1>Count: {count}</h1>
<button onClick={increment}>
Increment
</button>
<button onClick={decrement}>
Decrement
</button>
</div>
);
}
export default App;
How Custom Hooks Work?
Custom Hooks do not share state automatically between components.
Every time a custom hook is used, a separate state instance is created.
Example
If two components use useCounter():
const counter1 = useCounter()
const counter2 = useCounter();
Both counters will have independent states.
Custom Hook with useEffect
Custom Hooks often use useEffect for API calls, event listeners, or timers.
Example: Window Width Hook
useWindowWidth.js
import { useState, useEffect } from "react";
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;
}
export default useWindowWidth;
Using the Hook
import React from "react";
import useFetch from "./useFetch";
function Users() {
const { data, loading, error } =
useFetch("https://jsonplaceholder.typicode.com/users");
if (loading) {
return <h2>Loading...</h2>;
}
if (error) {
return <h2>Error occurred</h2>;
}
return (
<div>
<h1>User List</h1>
{data.map((user) => (
<p key={user.id}>
{user.name}
</p>
))}
</div>
);
}
export default App;
Real-Life Example of Custom Hooks
Scenario: Fetching API Data
In many applications, developers repeatedly write API fetching logic.
Custom Hooks can simplify this process.
Create API Hook
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(() => {
fetch(url)
.then((response) => response.json())
.then((result) => {
setData(result);
setLoading(false);
})
.catch((err) => {
setError(err);
setLoading(false);
});
}, [url]);
return { data, loading, error };
}
export default useFetch;
Using API Hook
import React from "react";
import useFetch from "./useFetch";
function Users() {
const { data, loading, error } =
useFetch("https://jsonplaceholder.typicode.com/users");
if (loading) {
return <h2>Loading...</h2>;
}
if (error) {
return <h2>Error occurred</h2>;
}
return (
<div>
<h1>User List</h1>
{data.map((user) => (
<p key={user.id}>
{user.name}
</p>
))}
</div>
);
}
export default Users;
Advantages of Custom Hooks
| Feature | Benefit |
|---|---|
| Reusability | Same logic used multiple times |
| Cleaner Components | Less clutter in components |
| Better Organization | Logic stays separated |
| Easy Testing | Hook logic can be tested independently |
| Scalability | Helpful in large projects |
Common Mistakes in Custom Hooks
1. Not Starting Hook Name with use
Wrong:
function counterHook() {
}
Correct:
function useCounter() {
}
2. Calling Hooks Conditionally
Wrong:
if (show) {
useState(0);
}
Hooks must always run in the same order.
3. Forgetting Cleanup in useEffect
Wrong:
useEffect(() => {
window.addEventListener("resize", handleResize);
}, []);
Correct:
useEffect(() => {
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
4. Returning Too Many Unnecessary Values
Keep hooks simple and focused.
Bad Practice:
return {
count,
increment,
decrement,
reset,
darkMode,
theme,
users
};
5. Creating Very Large Hooks
A hook should solve one problem only.
Best Practices for Custom Hooks
Use Descriptive Names
Good:
useFetchData()
Bad:
useData()
Keep Hooks Small
Each hook should have one responsibility.
Reuse Existing Hooks
Custom hooks are usually combinations of React hooks.
Use Proper Cleanup
Always clean timers, subscriptions, and event listeners.
Avoid Unnecessary Re-renders
Use dependency arrays carefully in useEffect.
Custom Hook vs Normal Function
| Feature | Normal Function | Custom Hook |
|---|---|---|
| Uses React Hooks | No | Yes |
| Starts with use | No | Yes |
| React State Support | No | Yes |
| Lifecycle Features | No | Yes |
| Reusable Logic | Limited | Excellent |
Conclusion
Custom Hooks are one of the most powerful features in ReactJS. They help developers write reusable, cleaner, and more maintainable code.
Instead of duplicating logic across components, developers can move reusable functionality into custom hooks and use it anywhere in the application.
Custom Hooks improve:
- Code organization
- Reusability
- Readability
- Scalability