Popular Searches
Popular Course Categories
Popular Courses

Custom Hooks

Custom Hooks

Advanced React Hooks

React Custom Hooks – Complete Notes

Custom Hooks are JavaScript functions that allow developers to reuse stateful logic across multiple React components. They are created by combining built-in React Hooks such as useState, useEffect, useContext, and other Hooks to implement reusable functionality.

Course Link: JustAcademy React JS Training Course

Course Demo: Register for React JS Course Demo


1. What Are Custom Hooks?

A Custom Hook is a user-defined function whose name starts with the word use followed by a capital letter. It can call built-in React Hooks and return values, functions, objects, arrays, or other data required by a component.

Custom Hooks help developers separate reusable logic from UI components, reduce duplicated code, and make applications easier to maintain.

Example

import { useState } from 'react';
function useCounter() {
  const [count, setCount] = useState(0);
  const increment = () => setCount(previous => previous + 1);
  const decrement = () => setCount(previous => previous - 1);
  return { count, increment, decrement };
}
export default useCounter;

Explanation: The useCounter Custom Hook manages counter state and returns the count value and two functions. Any React function component can use this Hook to reuse the same counter logic.


2. Why Do We Need Custom Hooks?

As React applications grow, multiple components may need the same functionality, such as form handling, fetching data, tracking online status, or managing a counter. Without Custom Hooks, developers may repeat the same logic in different components.

Custom Hooks allow developers to extract this logic into one reusable function.

  • Code reusability: Reuse logic across multiple components.
  • Less duplication: Avoid writing the same state and effect logic repeatedly.
  • Better organization: Separate business logic from UI presentation.
  • Easy maintenance: Update reusable logic in one place.
  • Improved readability: Keep components focused on displaying the interface.
  • Testability: Make reusable logic easier to reason about and test through components or Hook-testing utilities.

3. Syntax of a Custom Hook

function useCustomHook() {
  // Call built-in React Hooks here
  // Write reusable logic
  return value;
}
export default useCustomHook;

To use a Custom Hook, import it into a React component and call it at the top level of the component.

import useCustomHook from './useCustomHook';
function App() {
  const value = useCustomHook();
  return <div>{value}</div>;
}
export default App;

Important points:

  • The name should start with use, such as useCounter or useFetch.
  • A Custom Hook can call other built-in or custom Hooks.
  • A Custom Hook does not need to return JSX.
  • It can return a value, an object, an array, or functions.
  • It must follow the Rules of Hooks.

4. Custom Hooks vs Regular Functions

Feature Custom Hook Regular Function
Naming Starts with use followed by a capital letter. Can have any valid JavaScript function name.
React Hooks Can call other Hooks. Must not call React Hooks.
Purpose Reuses stateful React logic. Performs general JavaScript operations.
Return value Can return state, values, functions, objects, or arrays. Can return any JavaScript value.
Example useCounter() calculateTotal()

5. Rules of Custom Hooks

Custom Hooks must follow the same Rules of Hooks as built-in Hooks.

  1. Use the use prefix: Name Hooks like useCounter, useForm, or useOnlineStatus.
  2. Call Hooks at the top level: Do not call Hooks inside loops, conditions, or nested functions.
  3. Call from React functions: Call Custom Hooks from React function components or other Custom Hooks, not from ordinary JavaScript functions.
  4. Keep logic pure: Avoid unintended side effects during rendering.
  5. Handle dependencies correctly: When using effects or memoization, specify the required reactive dependencies.
  6. Clean up effects: Remove event listeners, clear timers, and release subscriptions when necessary.

Incorrect Example

function App({ isLoggedIn }) {
  if (isLoggedIn) {
    const [count, setCount] = useState(0);
  }
  return <div>Welcome</div>;
}

This is incorrect because a Hook is called conditionally.

Correct Example

import { useState } from 'react';
function App({ isLoggedIn }) {
  const [count, setCount] = useState(0);
  if (!isLoggedIn) {
    return <div>Please log in</div>;
  }
  return <div>Count: {count}</div>;
}

6. Example 1: Creating a useCounter Custom Hook

This example creates reusable counter logic with increment, decrement, and reset functionality.

File: useCounter.js

import { useState } from 'react';
function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue);
  const increment = () => setCount(previous => previous + 1);
  const decrement = () => setCount(previous => previous - 1);
  const reset = () => setCount(initialValue);
  return { count, increment, decrement, reset };
}
export default useCounter;

File: App.jsx

import useCounter from './useCounter';
function App() {
  const { count, increment, decrement, reset } = useCounter(0);
  return (
    <div>
      <h1>Counter: {count}</h1>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
      <button onClick={reset}>Reset</button>
    </div>
  );
}
export default App;

Output: The page displays the counter and three buttons. Increment increases the value, Decrement decreases it, and Reset restores the initial value.

Learning point: You can use useCounter(5) in another component to start a separate counter at 5. Each call has its own independent state.


7. Example 2: Creating a useToggle Custom Hook

A toggle Hook switches a Boolean value between true and false. It is useful for showing or hiding content, opening menus, and controlling simple UI states.

File: useToggle.js

import { useState } from 'react';
function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue);
  const toggle = () => setValue(previous => !previous);
  return [value, toggle];
}
export default useToggle;

File: App.jsx

import useToggle from './useToggle';
function App() {
  const [isVisible, toggleVisibility] = useToggle(false);
  return (
    <div>
      <button onClick={toggleVisibility}>
        {isVisible ? 'Hide Message' : 'Show Message'}
      </button>
      {isVisible && <p>Welcome to React JS!</p>}
    </div>
  );
}
export default App;

Explanation: The button calls toggleVisibility. When the value is true, the message appears; when false, the message is hidden.


8. Example 3: Creating a useFormInput Custom Hook

This Custom Hook manages the value of an input field and provides a reusable change handler.

File: useFormInput.js

import { useState } from 'react';
function useFormInput(initialValue = '') {
  const [value, setValue] = useState(initialValue);
  const onChange = event => setValue(event.target.value);
  return { value, onChange, setValue };
}
export default useFormInput;

File: App.jsx

import useFormInput from './useFormInput';
function App() {
  const name = useFormInput('');
  const email = useFormInput('');
  const handleSubmit = event => {
    event.preventDefault();
    alert(`Name: ${name.value}, Email: ${email.value}`);
  };
  return (
    <form onSubmit={handleSubmit}>
      <input
        value={name.value}
        onChange={name.onChange}
        placeholder="Enter your name"
        required
      />
      <input
        type="email"
        value={email.value}
        onChange={email.onChange}
        placeholder="Enter your email"
        required
      />
      <button type="submit">Submit</button>
    </form>
  );
}
export default App;

Explanation: The same Hook manages both the name and email fields. Each call maintains its own independent state, making the logic reusable.


9. Example 4: Creating a useEffect-Based useDocumentTitle Hook

A Custom Hook can combine built-in Hooks to synchronize the browser document title with a value.

File: useDocumentTitle.js

import { useEffect } from 'react';
function useDocumentTitle(title) {
  useEffect(() => {
    const previousTitle = document.title;
    document.title = title;
    return () => {
      document.title = previousTitle;
    };
  }, [title]);
}
export default useDocumentTitle;

File: App.jsx

import useDocumentTitle from './useDocumentTitle';
function App() {
  useDocumentTitle('My React Application');
  return <h1>Welcome to My Application</h1>;
}
export default App;

Explanation: The Hook updates the browser tab title when the supplied title changes. Its cleanup restores the previous title when the component using the Hook is removed or before the effect is rerun. This Hook is intended for browser-based React applications.


10. Example 5: Creating a useOnlineStatus Custom Hook

This Hook tracks whether the browser reports that the device is online. It demonstrates how to reuse state and browser event listeners across components.

File: useOnlineStatus.js

import { useEffect, useState } from 'react';
function useOnlineStatus() {
  const [isOnline, setIsOnline] = useState(
    typeof navigator === 'undefined' ? true : navigator.onLine
  );
  useEffect(() => {
    const handleOnline = () => setIsOnline(true);
    const handleOffline = () => setIsOnline(false);
    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);
    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);
  return isOnline;
}
export default useOnlineStatus;

File: App.jsx

import useOnlineStatus from './useOnlineStatus';
function App() {
  const isOnline = useOnlineStatus();
  return (
    <h2>
      {isOnline ? 'You are online' : 'You are offline'}
    </h2>
  );
}
export default App;

Explanation: The Hook listens for the browser's online and offline events and updates the state. The cleanup removes the event listeners when the component using the Hook unmounts. The browser's online status does not guarantee that a website or server is reachable.


11. Example 6: Creating a useFetch Custom Hook

A data-fetching Custom Hook can reuse loading, error, and response handling across components. The following example uses the Fetch API and aborts an outstanding request when the URL changes or the component unmounts.

File: useFetch.js

import { useEffect, useState } from 'react';
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  useEffect(() => {
    const controller = new AbortController();
    async function fetchData() {
      setLoading(true);
      setError(null);
      try {
        const response = await fetch(url, {
          signal: controller.signal
        });
        if (!response.ok) {
          throw new Error(`HTTP error: ${response.status}`);
        }
        const result = await response.json();
        setData(result);
      } catch (err) {
        if (err.name !== 'AbortError') {
          setError(err.message || 'Something went wrong');
        }
      } finally {
        if (!controller.signal.aborted) {
          setLoading(false);
        }
      }
    }
    fetchData();
    return () => controller.abort();
  }, [url]);
  return { data, loading, error };
}
export default useFetch;

File: App.jsx

import useFetch from './useFetch';
function App() {
  const { data, loading, error } = useFetch(
    'https://jsonplaceholder.typicode.com/users'
  );
  if (loading) return <p>Loading users...</p>;
  if (error) return <p>Error: {error}</p>;
  return (
    <ul>
      {data.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}
export default App;

Explanation: The Hook fetches data from the supplied URL and returns the response data, loading state, and error state. When the URL changes, the previous request is aborted and a new request starts. This example is intended for a browser environment with network access.


12. How Custom Hooks Share Logic

Custom Hooks share the implementation of stateful logic, not one shared state instance. Each component that calls a Custom Hook gets its own independent Hook state unless the logic deliberately connects to a shared store or context.

Example

function CounterOne() {
  const { count, increment } = useCounter();
  return <button onClick={increment}>Counter One: {count}</button>;
}
function CounterTwo() {
  const { count, increment } = useCounter();
  return <button onClick={increment}>Counter Two: {count}</button>;
}

Result: Counter One and Counter Two each maintain their own count. Clicking one does not automatically change the other.

To share actual state between components, consider lifting state to a common parent, using Context, or using an appropriate shared state-management solution.


13. Custom Hooks with Context API

A Custom Hook can wrap useContext to make shared application data easier to access.

import { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
function useTheme() {
  return useContext(ThemeContext);
}
function ThemeDisplay() {
  const theme = useTheme();
  return <p>Current theme: {theme}</p>;
}
function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemeDisplay />
    </ThemeContext.Provider>
  );
}
export default App;

Explanation: The useTheme Custom Hook hides the direct Context access. Components can use the Hook to read the theme without repeating the useContext(ThemeContext) expression.


14. Custom Hooks vs Built-in Hooks

Feature Built-in Hooks Custom Hooks
Created by React. Application developers or libraries.
Examples useState, useEffect, useContext. useCounter, useFetch, useOnlineStatus.
Purpose Provide React features. Combine and reuse logic for specific use cases.
Can call Hooks? They are the Hooks being called. Can call built-in Hooks and other Custom Hooks.

15. Advantages of Custom Hooks

  • Promote reusable code across components.
  • Reduce duplicated state and effect logic.
  • Improve component readability and organization.
  • Make application logic easier to maintain.
  • Encourage separation of UI and behavior.
  • Support consistent behavior across multiple components.
  • Can simplify testing of reusable behavior.

16. Limitations and Common Mistakes

  • Incorrect naming: A Hook that calls other Hooks should follow the use naming convention.
  • Conditional Hook calls: Never call Hooks inside conditions or loops.
  • Missing dependencies: Incorrect effect dependencies may cause stale values or synchronization bugs.
  • Unnecessary abstraction: Do not create a Custom Hook for every tiny piece of logic if it makes the code harder to understand.
  • Assuming state is shared: Calling the same Custom Hook in two components normally creates independent state.
  • Missing cleanup: Effects that subscribe to events or create timers should clean them up when necessary.
  • Overly generic Hooks: Prefer Hooks with clear purposes, such as useOnlineStatus, rather than vague wrappers around lifecycle behavior.

17. When Should You Create a Custom Hook?

Create a Custom Hook when multiple components need the same stateful behavior or when extracting logic makes a component easier to understand.

Use Case Suggested Custom Hook
Counter management useCounter
Show or hide a component useToggle
Managing form input useFormInput
Tracking network status useOnlineStatus
Fetching remote data useFetch
Reading theme settings useTheme
Managing browser document title useDocumentTitle

18. Best Practices for Custom Hooks

  • Choose descriptive names that explain the Hook's purpose.
  • Keep each Hook focused on a clear, reusable responsibility.
  • Return only the values and functions consumers need.
  • Follow the Rules of Hooks and React's purity requirements.
  • Specify effect dependencies correctly.
  • Clean up event listeners, timers, and subscriptions.
  • Do not assume separate Hook calls share state.
  • Use existing built-in Hooks when they already solve the problem.
  • Avoid extracting logic merely to reduce line count; focus on clearer and more reusable behavior.
  • Use the React Hooks ESLint rules to catch common mistakes.

19. Custom Hooks Interview Questions and Answers

Q1. What is a Custom Hook in React?

Answer: A Custom Hook is a JavaScript function that uses React Hooks to reuse stateful logic across React components.

Q2. Why do we use Custom Hooks?

Answer: They help reduce duplicated logic, improve readability, and make reusable behavior easier to maintain.

Q3. What naming convention should Custom Hooks follow?

Answer: Their names should start with use followed by a capital letter, such as useCounter.

Q4. Can a Custom Hook call another Hook?

Answer: Yes. A Custom Hook can call built-in Hooks and other Custom Hooks, provided all Hooks follow the Rules of Hooks.

Q5. Does a Custom Hook return JSX?

Answer: Not necessarily. It commonly returns values, functions, objects, or arrays that a component uses to render its UI.

Q6. Do two components using the same Custom Hook share state?

Answer: No. Each call normally has independent state. Shared state requires a separate mechanism, such as lifting state or Context.

Q7. Can we call a Custom Hook inside a condition?

Answer: No. Call it at the top level of a function component or another Custom Hook.

Q8. What is the difference between a Custom Hook and a regular function?

Answer: A Custom Hook can call React Hooks and reuse stateful logic. A regular function performs ordinary JavaScript operations and must not call React Hooks.

Q9. Can Custom Hooks use useEffect?

Answer: Yes. A Custom Hook can use useEffect to synchronize with external systems, such as browser events or network connections.

Q10. Should every repeated line of code become a Custom Hook?

Answer: No. Extract a Hook when doing so improves reuse, clarity, or separation of concerns. Simple duplication is sometimes easier to understand than an unnecessary abstraction.


20. Practice Exercises

  1. Create a useCounter Hook with increment, decrement, and reset functionality.
  2. Create a useToggle Hook to show and hide a message.
  3. Create a useFormInput Hook for name and email inputs.
  4. Create a useDocumentTitle Hook that updates the browser tab title.
  5. Create a useOnlineStatus Hook that listens to online and offline events.
  6. Create a useFetch Hook that handles loading, data, errors, and request cleanup.
  7. Use the same Custom Hook in two components and verify that their states are independent.

21. Quick Revision Checklist

  • Understand what Custom Hooks are and why they are useful.
  • Know the syntax and naming convention.
  • Understand the Rules of Hooks.
  • Know how to combine useState and useEffect inside a Custom Hook.
  • Understand that each Hook call normally has its own state.
  • Practice counter, toggle, form, online-status, and data-fetching Hooks.
  • Learn how to handle dependencies and clean up effects.

22. Official React Documentation


23. React JS Training Resources

Explore React JS concepts and practical application development through these course resources:

Conclusion

Custom Hooks are an important React feature for organizing and reusing stateful logic. By combining built-in Hooks into focused, reusable functions, developers can simplify components, reduce duplication, and maintain consistent behavior across an application. Follow the Rules of Hooks, manage dependencies carefully, and create Custom Hooks when they make code clearer and easier to maintain.

whatsapp