React Custom Hooks

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

Continue Learning React