Skip to content

useListeners() #200

@goldlabelapps

Description

@goldlabelapps

You can handle DOM events cleanly in React by wrapping them inside custom hooks. The pattern is always:

  1. Add the listener in useEffect
  2. Remove it in the cleanup
  3. Expose a simple API to components

Here are the two core patterns you’ll reuse.


Keystroke hook

import { useEffect } from "react";

export function useKey(key: string, handler: () => void) {
  useEffect(() => {
    function onKey(e: KeyboardEvent) {
      if (e.key.toLowerCase() === key.toLowerCase()) handler();
    }
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [key, handler]);
}

Usage:

useKey("d", () => dispatch(doSomething()));

Resize hook

import { useState, useEffect } from "react";

export function useWindowSize() {
  const [size, setSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight,
  });

  useEffect(() => {
    function onResize() {
      setSize({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    }
    window.addEventListener("resize", onResize);
    return () => window.removeEventListener("resize", onResize);
  }, []);

  return size;
}

Usage:

const { width, height } = useWindowSize();

General event hook pattern

export function useEvent<K extends keyof WindowEventMap>(
  type: K,
  handler: (e: WindowEventMap[K]) => void,
) {
  useEffect(() => {
    window.addEventListener(type, handler);
    return () => window.removeEventListener(type, handler);
  }, [type, handler]);
}

Usage:

useEvent("scroll", () => console.log("scrolling"));
useEvent("keydown", (e) => console.log(e.key));

How it all fits

Inside a component:

import { useKey } from "./useKey";

function MyComponent() {
  const dispatch = useDispatch();

  useKey("d", () => dispatch(openDebugPanel()));
  useKey("Escape", () => dispatch(closeDialogs()));

  return null;
}

This gives you a clean, reusable, idiomatic React pattern for attaching DOM listeners with proper cleanup.

Metadata

Metadata

Assignees

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions