7 React Custom Hooks I Always Use as a Front-End Developer ๐Ÿš€
Joodi

Joodi @joodi

About: ๐—™๐—ฟ๐—ผ๐—ป๐˜-๐—ฒ๐—ป๐—ฑ ๐—ฑ๐—ฒ๐˜ƒ๐—ฒ๐—น๐—ผ๐—ฝ๐—ฒ๐—ฟ sharing what I learn. I introduce new tools and ideas hereโ€”your support means the world! ๐Ÿš€

Joined:
Mar 2, 2024

7 React Custom Hooks I Always Use as a Front-End Developer ๐Ÿš€

Publish Date: Jan 17
177 34

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 ๐Ÿš€

Comments 34 total

  • Himanshu Sorathiya
    Himanshu Sorathiya Jan 18, 2025

    Very nice

    • Joodi
      JoodiJan 19, 2025

      Cheers, mate! Glad you liked it! ๐Ÿ˜Š

  • Shaktijeet Sahoo
    Shaktijeet SahooJan 18, 2025

    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.

    • Joodi
      JoodiJan 18, 2025

      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! ๐Ÿ˜Š

      • GRACE
        GRACEJan 20, 2025

        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.

  • Ruslan Gainutdinov
    Ruslan GainutdinovJan 19, 2025

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

    • Joodi
      JoodiJan 19, 2025

      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! ๐Ÿš€

  • Smit Patel
    Smit PatelJan 19, 2025

    Nice one mate

    • Joodi
      JoodiJan 19, 2025

      Thanks, mate! Glad you liked it! ๐Ÿ™Œ

  • Eustachio
    EustachioJan 19, 2025

    Very good! Thanks ๐Ÿ‘

    • Joodi
      JoodiJan 19, 2025

      Appreciate it, mate! Glad you liked it! ๐Ÿ‘๐Ÿ”ฅ

  • Umair
    Umair Jan 20, 2025

    Very good info, it was interesting to know and read.

    • Joodi
      JoodiJan 20, 2025

      Thank you! Glad you found it interesting and helpful! ๐Ÿ˜Š

  • Rohit Kumar Singh
    Rohit Kumar SinghJan 20, 2025

    Nice list

    • Joodi
      JoodiJan 20, 2025

      Thanks a lot! Happy you found it useful! ๐Ÿ˜Š

  • ramdasrao
    ramdasraoJan 20, 2025

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

    • Joodi
      JoodiJan 20, 2025

      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! ๐Ÿš€

  • Ayomide Emmanuel Akintan
    Ayomide Emmanuel AkintanJan 20, 2025

    Wonderful stuff โœจ
    Can't wait to begin implementing

    Thank you โœจ
    God bless you

    • Joodi
      JoodiJan 20, 2025

      Thank you so much! โœจ Hope it helps you outโ€”canโ€™t wait to see what you create! ๐Ÿ™Œ God bless you too! ๐Ÿ˜Š

  • Pollab
    Pollab Jan 20, 2025

    Great work

    • Joodi
      JoodiJan 20, 2025

      Thanks a lot! Glad you liked it! ๐Ÿ˜Š

  • Denes Papp
    Denes PappJan 20, 2025

    thanks, these are really great

    • Joodi
      JoodiJan 20, 2025

      Thanks a lot! Glad you think so! ๐Ÿ˜Š

  • Maher
    MaherJan 20, 2025

    A seed phrase, consisting of 12, 18, or 24 words, acts as the master password to your cryptocurrency wallet, enabling you to recover your funds if your wallet is lost or compromised. Itโ€™s the human-readable version of the private keys that prove your ownership of your crypto assets paybis.com/blog/glossary/seed-phrase/. The seed phrase is vital because it ensures wallet recovery, protects you against hacking by allowing you to swiftly transfer funds to a new wallet, and provides easy access across multiple devices. Always keep your seed phrase secure and never share it, as it's the key to your digital wealth. If you're looking to manage and exchange your crypto securely, Paybis is an excellent platform to get started with!

  • Rense Bakker
    Rense BakkerJan 20, 2025

    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.

    • Joodi
      JoodiJan 20, 2025

      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! ๐Ÿ˜Š

      • Corey K
        Corey KJan 21, 2025

        Lol this reply reads like sloppy ai

        • Joodi
          JoodiJan 21, 2025

          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.

  • Nick
    NickJan 20, 2025

    Shouldn't useMediaQuery utilize useLayoutEffect instead of useEffect so it gives the result before the browser paints the screen?

    • Joodi
      JoodiJan 20, 2025

      Thank you for pointing that out! Youโ€™re absolutely right that ๐˜‚๐˜€๐—ฒ๐—Ÿ๐—ฎ๐˜†๐—ผ๐˜‚๐˜๐—˜๐—ณ๐—ณ๐—ฒ๐—ฐ๐˜ could be more suitable for ๐˜‚๐˜€๐—ฒ๐— ๐—ฒ๐—ฑ๐—ถ๐—ฎ๐—ค๐˜‚๐—ฒ๐—ฟ๐˜† in cases where we want to ensure that the media query result is applied before the browser paints the screen. This would help avoid potential flickering or mismatched styles during the initial render.

      However, in most cases where the result doesn't immediately affect the layout or cause visual changes, ๐˜‚๐˜€๐—ฒ๐—˜๐—ณ๐—ณ๐—ฒ๐—ฐ๐˜ can still work fine without noticeable issues. That said, for critical UI updates tied to media queries, I agree that switching to ๐˜‚๐˜€๐—ฒ๐—Ÿ๐—ฎ๐˜†๐—ผ๐˜‚๐˜๐—˜๐—ณ๐—ณ๐—ฒ๐—ฐ๐˜ would be the better choice for ensuring a smoother user experience.

      Iโ€™ll consider updating the implementation to use ๐˜‚๐˜€๐—ฒ๐—Ÿ๐—ฎ๐˜†๐—ผ๐˜‚๐˜๐—˜๐—ณ๐—ณ๐—ฒ๐—ฐ๐˜ and highlight this distinction in the article. Thanks for the great suggestion! ๐Ÿ™Œ

  • Blake Neely
    Blake NeelyJan 22, 2025

    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..

    • Joodi
      JoodiJan 22, 2025

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

Add comment