Event - Custom Hooks

Mitchell - Nov 7 - - Dev Community

You can find all the code in this post at the repo Github.


Event-related React custom hooks challenges


useBodyScrollLock()

import { useLayoutEffect, useState } from "react";

function useBodyScrollLock() {
  useLayoutEffect(() => {
    const originalStyle = window.getComputedStyle(document.body).overflow;

    document.body.style.overflow = "hidden";

    return () => {
      document.body.style.overflow = originalStyle;
    };
  }, []);
}

function Modal({ onClose }) {
  useBodyScrollLock();

  return (
    <div
      style={{
        zIndex: 100,
        background: "rgba(0,0,0,0.5)",
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        height: "100vh",
      }}
    >
      <div style={{ padding: 20, background: "#fff", borderRadius: 8 }}>
        <h2>Modal Content</h2>
        <button onClick={onClose}>Close</button>
      </div>
    </div>
  );
}

export default function App() {
  const [modalOpen, setModalOpen] = useState(false);

  return (
    <div style={{ height: "200vh", textAlign: "center", paddingTop: 100 }}>
      <button onClick={() => setModalOpen(true)}>Open Modal</button>
      {modalOpen && <Modal onClose={() => setModalOpen(false)} />}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

useClickOutside()

import { useRef, useEffect } from "react";

function useClickOutside(callbackFn) {
  const ref = useRef(null);

  useEffect(() => {
    const click = ({ target }) => {
      if (target && ref.current && !ref.current.contains(target)) {
        callbackFn();
      }
    };

    document.addEventListener("mousedown", click);

    return () => {
      document.removeEventListener("mousedown", click);
    };
  }, []);

  return ref;
}

/* Usage example */

export default function App() {
  const ref = useClickOutside();

  return <button>ClickOutside: {String(ref.current)}</button>;
}
Enter fullscreen mode Exit fullscreen mode

useEventListener()

import { useEffect, useState } from "react";

function useEventListener(eventName, handler, element = window) {
  useEffect(() => {
    const isSupported = element && element.addEventListener;

    if (!isSupported) {
      return;
    }

    const eventListener = (event) => {
      handler(event);
    };

    element.addEventListener(eventName, eventListener);

    return () => {
      element.removeEventListener(eventName, eventListener);
    };
  }, [eventName, handler, element]);
}

// Usage example
export default function App() {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    setCount((prevCount) => prevCount + 1);
  };

  useEventListener("click", handleClick);

  return (
    <div>
      <h2>Click Count: {count}</h2>
      <p>Click anywhere in the window to increment the count.</p>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

useFocus()

import { useRef, useState, useEffect, useCallback } from "react";

function useFocus() {
  const [isFocus, setIsFocus] = useState(false);
  const focusRef = useRef(null);
  const handleFocus = useCallback(() => {
    setIsFocus(true);
  }, []);
  const handleBlur = useCallback(() => {
    setIsFocus(false);
  }, []);

  useEffect(() => {
    const node = focusRef.current;

    setIsFocus(document.activeElement === node);

    if (node) {
      node.addEventListener("focus", handleFocus);
      node.addEventListener("blur", handleBlur);
    }

    return () => {
      node.removeEventListener("focus", handleFocus);
      node.removeEventListener("blur", handleBlur);
    };
  }, [focusRef.current]);

  return [focusRef, isFocus];
}

/* Usage example */

export default function App() {
  const [focusRef, isFocus] = useFocus();

  return (
    <div>
      <input ref={focusRef} value={isFocus} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

useHover()

import { useState, useRef, useEffect, useCallback } from "react";

function useHover() {
  const [isHovered, setIsHovered] = useState(false);
  const ref = useRef(null);
  const handleMouseEnter = useCallback(() => {
    setIsHovered(true);
  }, []);
  const handleMouseLeave = useCallback(() => {
    setIsHovered(false);
  }, []);

  useEffect(() => {
    setIsHovered(false);

    const node = ref.current;

    if (node) {
      node.addEventListener("mouseenter", handleMouseEnter);
      node.addEventListener("mouseleave", handleMouseLeave);

      return () => {
        node.removeEventListener("mouseenter", handleMouseEnter);
        node.removeEventListener("mouseleave", handleMouseLeave);
      };
    }
  }, [ref.current, handleMouseEnter, handleMouseLeave]);

  return [ref, isHovered];
}

/* Usage example */

export default function App() {
  const [inputRef, isHovered] = useHover();

  return (
    <div>
      <input ref={inputRef} value={isHovered} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

useKeyPress()

import { useState, useEffect, useCallback } from "react";

function useKeyPress(targetKey) {
  const [keyPressed, setKeyPressed] = useState(false);
  const handleKeyDown = useCallback((event) => {
    if (event.key === targetKey) {
      setKeyPressed(true);
    }
  }, []);
  const handleKeyUp = useCallback((event) => {
    if (event.key === targetKey) {
      setKeyPressed(false);
    }
  }, []);

  useEffect(() => {
    window.addEventListener("keydown", handleKeyDown);
    window.addEventListener("keyup", handleKeyUp);

    return () => {
      window.removeEventListener("keydown", handleKeyDown);
      window.removeEventListener("keyup", handleKeyUp);
    };
  }, []);

  return keyPressed;
}

function App() {
  const isSpacePressed = useKeyPress(" "); // Detect spacebar press
  const isEnterPressed = useKeyPress("Enter"); // Detect Enter key press

  return (
    <div>
      <h2>Key Press Demo</h2>
      <p>Press and hold the Spacebar or Enter key</p>
      <div>
        Spacebar pressed: <strong>{isSpacePressed ? "Yes" : "No"}</strong>
      </div>
      <div>
        Enter key pressed: <strong>{isEnterPressed ? "Yes" : "No"}</strong>
      </div>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

usePosition()

import { useState, useEffect, useRef } from "react";

function usePosition(ref) {
  const [position, setPosition] = useState({ top: 0, left: 0 });

  useEffect(() => {
    const updatePosition = () => {
      if (ref.current) {
        const { top, left } = ref.current.getBoundingClientRect();
        setPosition({ top, left });
      }
    };

    // Initial position update
    updatePosition();

    // Set up a resize observer to listen for position changes
    const resizeObserver = new ResizeObserver(updatePosition);
    if (ref.current) {
      resizeObserver.observe(ref.current);
    }

    // Cleanup function to disconnect the observer
    return () => {
      if (ref.current) {
        resizeObserver.unobserve(ref.current);
      }
    };
  }, [ref]); // Re-run when the ref changes

  return position;
}

function PositionDisplay() {
  const ref = useRef(null);
  const position = usePosition(ref);

  return (
    <div>
      <h2>Element Position:</h2>
      <div
        ref={ref}
        style={{
          width: "200px",
          height: "200px",
          backgroundColor: "lightblue",
          position: "relative",
        }}
      >
        Move me around!
      </div>
      <pre>{JSON.stringify(position, null, 2)}</pre>
    </div>
  );
}

export default function App() {
  return (
    <div>
      <h1>Listening to Element Position Changes</h1>
      <PositionDisplay />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

useToggle()

import { useState, useCallback } from 'react';

function useToggle(on) {
  const [isToggled, setIsToggled] = useState(on);

  const toggle = useCallback(() => {
    setIsToggled((t) => !t);
  }, []);

  return [isToggled, toggle];
}

/* Usage example */

export default function App() {
  const [isToggled, toggle] = useToggle(false);

  return (
    <div>
      <button onClick={toggle}>Click to see</button>
      {isToggled && <div>There is a hidden content</div>}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

useWindowResize()

import { useState, useEffect } from "react";

function useWindowResize() {
  const [windowSize, setWindowSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight,
  });

  useEffect(() => {
    const handleResize = () => {
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    };

    window.addEventListener("resize", handleResize);

    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []);

  return windowSize;
}

export default function App() {
  const { width, height } = useWindowResize();

  return (
    <div style={{ position: "fixed", top: 0, right: 0 }}>
      <h4>Window Size</h4>
      <p>Width: {width}px</p>
      <p>Height: {height}px</p>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

useWindowScroll()

import { useState, useEffect } from "react";

function useWindowScroll() {
  const [scrollPosition, setScrollPosition] = useState({
    scrollX: window.scrollX,
    scrollY: window.scrollY,
  });

  useEffect(() => {
    const handleScroll = () => {
      setScrollPosition({
        scrollX: window.scrollX,
        scrollY: window.scrollY,
      });
    };

    window.addEventListener("scroll", handleScroll, { passive: true });

    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, [handleScroll]);

  return scrollPosition;
}

// Usage example

function App() {
  const { scrollX, scrollY } = useWindowScroll();

  return (
    <div
      style={{
        position: "fixed",
        top: 10,
        right: 10,
        background: "white",
        padding: 10,
      }}
    >
      <p>Scroll X: {scrollX.toFixed(0)}px</p>
      <p>Scroll Y: {scrollY.toFixed(0)}px</p>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Reference

. . . . . . . . . . . . . . . . . . . . . . . . . . . . .