DEV Community

Joodi
Joodi

Posted on

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.

Top comments (0)