DEV Community

Cover image for Creating Custom Hooks in React: A Complete Guide for Developers
Shefali
Shefali

Posted on • Edited on • Originally published at shefali.dev

Creating Custom Hooks in React: A Complete Guide for Developers

In React development, custom hooks are a powerful way to reuse logic across components. Creating custom hooks helps you write cleaner, more maintainable code by encapsulating common functionality.

In this post, you’ll learn how to create and use custom hooks in React.

Before we get started, don’t forget to subscribe to my newsletter!
Get the latest tips, tools, and resources to level up your web development skills delivered straight to your inbox. Subscribe here!

Now, let’s jump right into it!

What Are Custom Hooks in React?

A custom hook is a function that provides reusable logic by using React’s built-in hooks (useState, useEffect, etc.).

Its name starts with “use” (for example: useAuth, useFetch, etc.), so that React knows it’s a hook.

Custom hooks increase reusability and maintainability.


Steps for Creating Custom Hooks in React

Here’s the step-by-step guide for creating a custom counter hook:

Step 1: Create a Hook Function

function useCounter() {

}

export default useCounter;
Enter fullscreen mode Exit fullscreen mode

Here,

  • The function name starts with “use”, which is a convention for hooks.
  • This ensures React treats it as a hook and applies hook rules properly.

Step 2: Add State and Functions

import { useState } from "react";

function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue);

  const increment = () => setCount(count + 1);
  const decrement = () => setCount(count - 1);
  const reset = () => setCount(initialValue);

}

export default useCounter;
Enter fullscreen mode Exit fullscreen mode

Here,

  • useState is used to manage the count state.
  • increment, decrement, and reset functions modify the count state.
  • This logic is reusable across multiple components.

Step 3: Return the values or functions that you need

import { useState } from "react";

function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue);

  const increment = () => setCount(count + 1);
  const decrement = () => setCount(count - 1);
  const reset = () => setCount(initialValue);

  return { count, increment, decrement, reset };
}

export default useCounter;
Enter fullscreen mode Exit fullscreen mode

Here,

  • The custom hook now returns an object containing count and update functions.
  • This allows any component to use these values and functions by destructuring.

Step 4: Use the custom hook in the counter component

import React from "react";
import useCounter from "./useCounter";

function CounterComponent() {
  const { count, increment, decrement, reset } = useCounter(5);

  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
      <button onClick={reset}>Reset</button>
    </div>
  );
}

export default CounterComponent;
Enter fullscreen mode Exit fullscreen mode

Here,

  • The useCounter hook is imported and used in CounterComponent.
  • The initial value 5 is passed as an argument.
  • The count state and functions are extracted using object destructuring.
  • Buttons are added to interact with the state using the hook’s functions.

By using this custom hook, you can reuse the same counter-logic in multiple components.


Why Use Custom Hooks in React?

Custom hooks are useful:

  • Reusability increases.
  • Code readability and maintainability improve.
  • Reduces the need to write the same logic again and again.

Best Practices for Creating Custom Hooks

  • Always start the name of the custom hook with “use” (useCounter, useFetch, etc.).
  • To manage state and side effects, use built-in hooks (useState, useEffect, etc.).
  • Try to create hooks that are reusable and generic.
  • If a custom hook is only needed in a single component, then writing that in a separate file is not required.

Real-World Examples of Custom Hooks

Here are some of the custom hooks that are useful in real-world applications.

useLocalStorage – For Storing Data

If you want to store data in local storage, then it’s a good practice to create a custom hook instead of writing localStorage.setItem and localStorage.getItem in each component.

import { useState, useEffect } from "react";

function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    const savedValue = localStorage.getItem(key);
    return savedValue ? JSON.parse(savedValue) : initialValue;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue];
}

export default useLocalStorage;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • Retrieves and stores values in localStorage automatically.
  • Uses useState to manage stored data.
  • Uses useEffect to update localStorage when the value changes.

Now, you can use this in any component.

import React from "react";
import useLocalStorage from "./useLocalStorage";

function ThemeComponent() {
  const [theme, setTheme] = useLocalStorage("theme", "light");

  return (
    <div>
      <h2>Current Theme: {theme}</h2>
      <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
        Toggle Theme
      </button>
    </div>
  );
}

export default ThemeComponent;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • Uses useLocalStorage to store and retrieve the theme.
  • Clicking the button toggles between “light” and “dark” mode.

useFetch – To Make API Calls Easy

You can create a custom hook for handling API calls instead of writing fetch() in each component.

import { useState, useEffect } from "react";

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        if (!response.ok) throw new Error("Failed to fetch data");
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
}

export default useFetch;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • Manages API calls with fetch inside useEffect.
  • Handles loading, data, and error states.
  • Fetches data when the URL changes.

Now, you can use this in any component.

import React from "react";
import useFetch from "./useFetch";

function UsersComponent() {
  const { data, loading, error } = useFetch("https://jsonplaceholder.typicode.com/users");

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;

  return (
    <div>
      <h2>Users List</h2>
      <ul>
        {data.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

export default UsersComponent;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • Fetches and displays a list of users.
  • Handles loading and error states for a smooth UI.
  • Uses useFetch to keep API logic reusable.

🎯Wrapping Up

That’s all for today!

For paid collaboration connect with me at : [email protected]

I hope this post helps you.

If you found this post helpful, here’s how you can support my work:
Buy me a coffee – Every little contribution keeps me motivated!
📩 Subscribe to my newsletter – Get the latest tech tips, tools & resources.
𝕏 Follow me on X (Twitter) – I share daily web development tips & insights.

Keep coding & happy learning!

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.