Back home

Custom React hooks

This is a set of hooks I often need in my projects.

useOnClickOutside

Detect when you clic outside of an element and accept an array of element's ref to ignore.

import { useState, useEffect, useRef } from "react";
 
function useOnClickOutside(ref, handler, execptions = []) {
  useEffect(() => {
    const listener = (event) => {
      if (
        !ref.current ||
        ref.current.contains(event.target) ||
        execptions.some((el) => el.current.contains(event.target))
      ) {
        return;
      }
 
      handler(event);
    };
 
    document.addEventListener("mousedown", listener);
    document.addEventListener("touchstart", listener);
 
    return () => {
      document.removeEventListener("mousedown", listener);
      document.removeEventListener("touchstart", listener);
    };
  }, [ref, handler]);
}

useScrollBreakpoint

import { useState, useEffect, useRef } from "react";
 
export function useScrollBreakpoint(breakpoint) {
  const [trigger, setTrigger] = useState(false);
  const previousScroll = useRef(window.scrollY);
 
  function handleScroll() {
    const currScroll = parseInt(window.scrollY);
    if (previousScroll.current <= breakpoint && currScroll > breakpoint) {
      previousScroll.current = currScroll;
      setTrigger(true);
    }
    if (previousScroll.current > breakpoint && currScroll <= breakpoint) {
      previousScroll.current = currScroll;
      setTrigger(false);
    }
  }
 
  useEffect(() => {
    // Add event listener
    window.addEventListener("scroll", handleScroll);
 
    // Call handler right away so state gets updated with initial scroll
    handleScroll();
 
    // Remove event listener on cleanup
    return () => window.removeEventListener("scroll", handleScroll);
  }, []); // Empty array ensures that effect is only run on mount
 
  return trigger;
}

useIsMobile

import { useState, useEffect } from "react";
 
export function useIsMobile() {
  const [isMobile, setIsMobile] = useState(false);
 
  function handleResize() {
    setIsMobile(
      parseInt(window.innerWidth) <=
        parseInt(process.env.REACT_APP_RWD_BREAKPOINT)
    );
  }
 
  useEffect(() => {
    // Add event listener
    window.addEventListener("resize", handleResize);
 
    // Call handler right away so state gets updated with initial window size
    handleResize();
 
    // Remove event listener on cleanup
    return () => window.removeEventListener("resize", handleResize);
  }, []); // Empty array ensures that effect is only run on mount
 
  return isMobile;
}