DEV Community

Cover image for How to Build a Custom Hook in React JS
Ghulam Murtaza for epicX

Posted on โ€ข Edited on

How to Build a Custom Hook in React JS

๐Ÿš€ Introduction

React has revolutionized frontend development with its component-based architecture. One of its most powerful features is custom hooks, which allow developers to encapsulate and reuse logic efficiently. If you find yourself using useState, useEffect, or useRef repeatedly across multiple components, creating a custom hook can significantly improve code organization and reusability.

๐Ÿ“Œ In this article, weโ€™ll cover:

  • ๐ŸŽฏ What custom hooks are and why theyโ€™re useful
  • ๐Ÿ› ๏ธ A step-by-step guide to creating a custom hook
  • ๐Ÿ“Œ Real-world examples of practical custom hooks
  • โœ… Best practices for writing custom hooks

๐Ÿค” What is a Custom Hook in React?

A custom hook is a JavaScript function that starts with use (e.g., useCustomHook) and encapsulates reusable stateful logic using Reactโ€™s built-in hooks. Instead of repeating logic in multiple components, we can extract it into a custom hook and use it across our application.

๐Ÿ’ก Example Use Case: Suppose you need to interact with localStorage in multiple components. Instead of writing localStorage.getItem() and localStorage.setItem() repeatedly, you can create a custom hook to handle this logic.


๐Ÿ—๏ธ How to Create a Custom Hook

To create a custom hook, follow these steps:

  • ๐Ÿท๏ธ Define a function with a name starting with use.
  • ๐Ÿ”„ Use built-in hooks like useState, useEffect, or useContext inside the function.
  • ๐Ÿ“ค Return values or functions that can be used by components.
  • ๐Ÿ“Œ Import and use the custom hook in your components.

Letโ€™s now build some practical custom hooks that solve real-world problems.


Example 1: ๐Ÿ—„๏ธ useLocalStorage (Managing Local Storage in React)

Local storage is commonly used to persist user preferences or session data. Letโ€™s create a custom hook for handling local storage.

Step 1: ๐Ÿ› ๏ธ Create the Hook (useLocalStorage.js)

import { useState, useEffect } from "react";

function useLocalStorage(key: string, initialValue: any) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error("Error accessing localStorage", error);
      return initialValue;
    }
  });

  useEffect(() => {
    try {
      window.localStorage.setItem(key, JSON.stringify(storedValue));
    } catch (error) {
      console.error("Error saving to localStorage", error);
    }
  }, [key, storedValue]);

  return [storedValue, setStoredValue];
}

export default useLocalStorage;
Enter fullscreen mode Exit fullscreen mode

Step 2: ๐ŸŽฏ Use It in a Component

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

function App() {
  const [name, setName] = useLocalStorage("username", "Guest");

  return (
    <div>
      <h2>Welcome, {name}! ๐ŸŽ‰</h2>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

โœ… Why This is Useful:

  • ๐Ÿ’พ Saves user input persistently in local storage.
  • ๐Ÿ”„ Retains values even after page refresh.

Example 2: ๐ŸŒ useFetch (Handling API Requests in React)

Fetching data is a common operation in React applications. Instead of writing fetch() calls repeatedly, we can create a reusable hook for API requests.

Step 1: ๐Ÿ› ๏ธ Create the Hook (useFetch.js)

import { useState, useEffect } from "react";

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

  useEffect(() => {
    setLoading(true);
    fetch(url)
      .then((res) => {
        if (!res.ok) throw new Error("Failed to fetch");
        return res.json();
      })
      .then((data) => setData(data))
      .catch((error) => setError(error))
      .finally(() => setLoading(false));
  }, [url]);

  return { data, loading, error };
}

export default useFetch;
Enter fullscreen mode Exit fullscreen mode

Step 2: ๐ŸŽฏ Use It in a Component

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

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

  if (loading) return <p>โณ Loading...</p>;
  if (error) return <p>โŒ Error: {error.message}</p>;

  return (
    <ul>
      {data.map((user: any) => (
        <li key={user.id}>๐Ÿ‘ค {user.name}</li>
      ))}
    </ul>
  );
}

export default UsersList;
Enter fullscreen mode Exit fullscreen mode

โœ… Why This is Useful:

  • ๐Ÿ”„ Centralizes API fetching logic.
  • โšก Handles loading and error states efficiently.

๐Ÿ† Best Practices for Writing Custom Hooks

  • ๐ŸŽฏ Keep Hooks Focused: Each hook should have a single responsibility.
  • ๐Ÿท๏ธ Follow the Naming Convention: Always start function names with use.
  • ๐Ÿ”„ Use Dependency Arrays Wisely: Avoid unnecessary re-renders in useEffect.
  • ๐Ÿ“Œ Ensure Hooks Are Reusable: Avoid using component-specific logic inside hooks.
  • ๐Ÿงช Test Custom Hooks: Use Jest and React Testing Library to ensure they work correctly.

๐ŸŽ‰ Conclusion

Custom hooks in React allow developers to abstract logic, improve code reusability, and enhance maintainability. Whether it's handling local storage, API calls, or dark mode toggling, custom hooks provide an elegant solution to avoid redundant code.

By following best practices and structuring hooks properly, you can build efficient and reusable components that scale well in any React project.

Would you like to explore more complex custom hooks? Let me know in the comments! ๐Ÿš€

Sentry image

See why 4M developers consider Sentry, โ€œnot bad.โ€

Fixing code doesnโ€™t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

The Most Contextual AI Development Assistant

Pieces.app image

Our centralized storage agent works on-device, unifying various developer tools to proactively capture and enrich useful materials, streamline collaboration, and solve complex problems through a contextual understanding of your unique workflow.

๐Ÿ‘ฅ Ideal for solo developers, teams, and cross-company projects

Learn more

๐Ÿ‘‹ Kindness is contagious

Please leave a โค๏ธ or a friendly comment on this post if you found it helpful!

Okay