DEV Community

Mitchell
Mitchell

Posted on

2

Event - Custom Hooks

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

AWS GenAI LIVE image

How is generative AI increasing efficiency?

Join AWS GenAI LIVE! to find out how gen AI is reshaping productivity, streamlining processes, and driving innovation.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more