DEV Community

Joodi
Joodi

Posted on • Edited on

144 7 4 5 9

7 React Custom Hooks I Always Use as a Front-End Developer 🚀

Custom hooks are not just a convenience in React—they're a game-changer for modular and maintainable code. They allow developers to encapsulate logic, manage state, and streamline complex functionalities in ways that weren’t possible before.

Image description

React’s powerful functional programming paradigm has redefined modern front-end development, paving the way for modular, maintainable, and reusable code. Among its many capabilities, custom hooks stand out as a key enabler for building smarter, cleaner components. Today, let’s dive into some essential custom hooks that every developer should have in their toolkit and learn how to implement them effectively.


  1. useFetch: Simplify API Calls 🌐 Fetching data is a common task in React. The useFetch hook abstracts repetitive logic, streamlining API calls and managing state elegantly.

Implementation:

import { useState, useEffect } from "react";

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

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      try {
        const response = await fetch(url);
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err as Error);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
}

export default useFetch;
Enter fullscreen mode Exit fullscreen mode

Usage:

const { data, loading, error } = useFetch<User[]>('/api/users');

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

return <ul>{data?.map(user => <li key={user.id}>{user.name}</li>)}</ul>;
Enter fullscreen mode Exit fullscreen mode

  1. useDebounce: Optimize Performance ⏳ Handling frequent user input, such as search or form fields, is made efficient with a debounce hook, reducing unnecessary renders and API calls.

Implementation:

import { useState, useEffect } from "react";

function useDebounce<T>(value: T, delay: number): T {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => setDebouncedValue(value), delay);

    return () => clearTimeout(handler);
  }, [value, delay]);

  return debouncedValue;
}

export default useDebounce;
Enter fullscreen mode Exit fullscreen mode

Usage:

const [searchTerm, setSearchTerm] = useState('');
const debouncedSearch = useDebounce(searchTerm, 500);

useEffect(() => {
  if (debouncedSearch) {
    // Trigger API or other actions
  }
}, [debouncedSearch]);
Enter fullscreen mode Exit fullscreen mode

  1. useToggle: Manage Boolean States Easily "Managing toggle states for modals, dropdowns, or theme switches is effortless with a custom useToggle hook, keeping your code clean and reusable.

Implementation:

import { useState } from "react";

function useToggle(initialState = false) {
  const [state, setState] = useState(initialState);

  const toggle = () => setState(prev => !prev);

  return [state, toggle] as const;
}

export default useToggle;
Enter fullscreen mode Exit fullscreen mode

Usage:

const [isModalOpen, toggleModal] = useToggle();

return (
  <div>
    <button onClick={toggleModal}>Toggle Modal</button>
    {isModalOpen && <p>Modal Content</p>}
  </div>
);
Enter fullscreen mode Exit fullscreen mode

  1. useLocalStorage: Persist Data Locally 📂 Storing and retrieving data from localStorage becomes seamless and reusable with a custom useLocalStorage hook.

Implementation:

import { useState } from "react";

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

  const setValue = (value: T) => {
    try {
      setStoredValue(value);
      window.localStorage.setItem(key, JSON.stringify(value));
    } catch (error) {
      console.error(error);
    }
  };

  return [storedValue, setValue] as const;
}

export default useLocalStorage;
Enter fullscreen mode Exit fullscreen mode

Usage:

const [theme, setTheme] = useLocalStorage('theme', 'light');

return (
  <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
    Toggle Theme
  </button>
);
Enter fullscreen mode Exit fullscreen mode

  1. usePrevious: Track Previous State 📜 Tracking a value's previous state is essential for comparisons and animations, easily achieved with a custom usePrevious hook.

Implementation:

import { useEffect, useRef } from "react";

function usePrevious<T>(value: T): T | undefined {
  const ref = useRef<T>();

  useEffect(() => {
    ref.current = value;
  }, [value]);

  return ref.current;
}

export default usePrevious;
Enter fullscreen mode Exit fullscreen mode

Usage:

const [count, setCount] = useState(0);
const prevCount = usePrevious(count);

return (
  <p>
    Now: {count}, Before: {prevCount}
  </p>
);
Enter fullscreen mode Exit fullscreen mode

  1. useClickOutside: Detect Outside Clicks 🖱️ Perfect for closing modals or dropdowns when clicking outside, using a custom useClickOutside hook for better user experience.

Implementation:

import { useEffect, useRef } from "react";

function useClickOutside(handler: () => void) {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (ref.current && !ref.current.contains(event.target as Node)) {
        handler();
      }
    };

    document.addEventListener('mousedown', handleClickOutside);
    return () => document.removeEventListener('mousedown', handleClickOutside);
  }, [handler]);

  return ref;
}

export default useClickOutside;
Enter fullscreen mode Exit fullscreen mode

Usage:

const ref = useClickOutside(() => setDropdownOpen(false));

return (
  <div ref={ref}>
    {dropdownOpen && <p>Dropdown Content</p>}
  </div>
);
Enter fullscreen mode Exit fullscreen mode

  1. useMediaQuery: Handle Responsive Design 📱 Managing media queries in React is simplified with a custom useMediaQuery hook, making responsive design more efficient.

Implementation:

import { useState, useEffect } from "react";

function useMediaQuery(query: string): boolean {
  const [matches, setMatches] = useState(false);

  useEffect(() => {
    const mediaQueryList = window.matchMedia(query);

    const updateMatch = () => setMatches(mediaQueryList.matches);
    updateMatch();

    mediaQueryList.addEventListener('change', updateMatch);
    return () => mediaQueryList.removeEventListener('change', updateMatch);
  }, [query]);

  return matches;
}

export default useMediaQuery;
Enter fullscreen mode Exit fullscreen mode

Usage:

const isMobile = useMediaQuery('(max-width: 768px)');

return <p>{isMobile ? 'Mobile View' : 'Desktop View'}</p>;
Enter fullscreen mode Exit fullscreen mode

_Custom hooks showcase React’s flexibility and power, enabling cleaner, reusable, and more maintainable code.
_

By leveraging custom hooks, developers can simplify complex functionality and create reusable, efficient code. The examples above demonstrate how these hooks elegantly solve common challenges.


I hope you found this helpful! I'd be happy if we connected on LinkedIn 🚀

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (33)

Collapse
 
deathcrafter profile image
Shaktijeet Sahoo

I'd prefer to use Tanstack Query over useFetch if I needed to improve performance because of the in-built caching.

Other than that, really useful hooks. Great read.

Collapse
 
joodi profile image
Joodi • Edited

You're totally right! TanStack Query is great for better performance with its caching and extra features. But for simpler cases, a custom hook like useFetch works just fine. Glad you liked the hook—thanks for the support! 😊

Collapse
 
gracogen profile image
GRACE • Edited

Hello Joodi. I'm Grace

I hope this doesn't sound intrusive.

I'm a newbie React Developer and I'm totally available for Volunteer work.

I'm ready to volunteer without a pay to improve on my skills.

Looking forward to hearing from you.

Thanks for the write up.

Collapse
 
brense profile image
Rense Bakker

The return values from your custom hooks will create a new object or array value on each render, which means they will fail referential equality checks. This becomes a problem if you want to use the values returned from your custom hooks in a useEffect hook for example. The useEffect would be triggered on each render, even if the data returned from your custom hooks hasn't changed.

Collapse
 
joodi profile image
Joodi

Thank you for the valuable feedback! You’re absolutely right. Returning new objects or arrays from custom hooks can lead to unnecessary re-renders, especially when used in 𝘂𝘀𝗲𝗘𝗳𝗳𝗲𝗰𝘁 or other hooks that rely on referential equality.

I’ll update the article to include 𝘂𝘀𝗲𝗠𝗲𝗺𝗼 to prevent unnecessary reference changes. This will ensure better performance by only updating the reference when the actual data changes. Thanks again for pointing this out! 😊

Collapse
 
corey_koelewyn_b9d5b59a2a profile image
Corey K

Lol this reply reads like sloppy ai

Thread Thread
 
joodi profile image
Joodi

Thanks for the feedback! I can assure you this was written with care—sometimes technical responses can come off as a bit formal. I appreciate your input and will keep it in mind to make my tone more natural in future replies.

Collapse
 
blakeneely profile image
Blake Neely

These are wonderfully easy to read and simple to implement. I especially liked the useFetch hook. With returning data, loading and error it mimics Tanstack Query or Apollo Client with GraphQL but without all of the heavy lifting—great for light weight use cases..

Collapse
 
joodi profile image
Joodi

Yeah, I found it interesting too! It's a nice lightweight solution, especially for simpler use cases. 😊

Collapse
 
ramdasrao profile image
ramdasrao

Excellent. Perhaps there is a package with all these hooks?

Collapse
 
joodi profile image
Joodi

Thank you! Glad you liked it! 😊 There isn’t a package with all these hooks yet, but that’s a great idea—maybe it’s time to create one! 🚀

Collapse
 
ramdasrao profile image
ramdasrao

Looking forward to it.

Collapse
 
emmy-akints profile image
Ayomide Emmanuel Akintan • Edited

Wonderful stuff ✨
Can't wait to begin implementing

Thank you ✨
God bless you

Collapse
 
joodi profile image
Joodi

Thank you so much! ✨ Hope it helps you out—can’t wait to see what you create! 🙌 God bless you too! 😊

Collapse
 
emmy-akints profile image
Ayomide Emmanuel Akintan

Amen
You're welcome ✨

Collapse
 
rksingh620 profile image
Rohit Kumar Singh

Nice list

Collapse
 
joodi profile image
Joodi

Thanks a lot! Happy you found it useful! 😊

Collapse
 
pollabd profile image
Pollab

Great work

Collapse
 
joodi profile image
Joodi

Thanks a lot! Glad you liked it! 😊

Collapse
 
huksley profile image
Ruslan Gainutdinov

I like how simple and useful your useDebounce hook! For fetch I use SWR library which is tiny and supports many user cases.

Collapse
 
joodi profile image
Joodi • Edited

Thank you! I'm glad you liked the simplicity of the 𝘂𝘀𝗲𝗗𝗲𝗯𝗼𝘂𝗻𝗰𝗲 hook. SWR is an awesome library—lightweight and packed with features. It's a great choice for handling fetches with caching and other advanced use cases! 🚀

Collapse
 
eustachi0 profile image
Eustachio

Very good! Thanks 👍

Collapse
 
joodi profile image
Joodi

Appreciate it, mate! Glad you liked it! 👍🔥

Collapse
 
wizard798 profile image
Wizard

Very nice

Collapse
 
joodi profile image
Joodi

Cheers, mate! Glad you liked it! 😊

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

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Immerse yourself in a wealth of knowledge with this piece, supported by the inclusive DEV Community—every developer, no matter where they are in their journey, is invited to contribute to our collective wisdom.

A simple “thank you” goes a long way—express your gratitude below in the comments!

Gathering insights enriches our journey on DEV and fortifies our community ties. Did you find this article valuable? Taking a moment to thank the author can have a significant impact.

Okay