Skip to content

Instantly share code, notes, and snippets.

@MarlonPassos-git
Created March 13, 2025 21:12
Show Gist options
  • Select an option

  • Save MarlonPassos-git/81e5ac981d04ffa845b26bd4bdd8d2e3 to your computer and use it in GitHub Desktop.

Select an option

Save MarlonPassos-git/81e5ac981d04ffa845b26bd4bdd8d2e3 to your computer and use it in GitHub Desktop.
react hook useUserIteration.ts
/* eslint-disable @typescript-eslint/ban-ts-comment */
import type { RefObject } from "react";
import { useEffect, useRef } from "react";
import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect";
type NewEvent = "storageUpdate" | "local-storage";
// MediaQueryList Event based useEventListener interface
function useEventListener<K extends keyof MediaQueryListEventMap>(
eventName: K | NewEvent,
handler: (event: MediaQueryListEventMap[K]) => void,
element: RefObject<MediaQueryList>,
options?: boolean | AddEventListenerOptions,
): void;
// Window Event based useEventListener interface
function useEventListener<K extends keyof WindowEventMap>(
eventName: K | NewEvent,
handler: (event: WindowEventMap[K]) => void,
element?: undefined,
options?: boolean | AddEventListenerOptions,
): void;
// Element Event based useEventListener interface
function useEventListener<
K extends keyof HTMLElementEventMap,
T extends HTMLElement = HTMLDivElement,
>(
eventName: K | NewEvent,
handler: (event: HTMLElementEventMap[K]) => void,
element: RefObject<T>,
options?: boolean | AddEventListenerOptions,
): void;
// Document Event based useEventListener interface
function useEventListener<K extends keyof DocumentEventMap>(
eventName: K | NewEvent,
handler: (event: DocumentEventMap[K]) => void,
element: RefObject<Document>,
options?: boolean | AddEventListenerOptions,
): void;
function useEventListener<
KW extends keyof WindowEventMap,
KH extends keyof HTMLElementEventMap,
KM extends keyof MediaQueryListEventMap,
T extends HTMLElement | MediaQueryList | undefined = undefined,
>(
eventName: KW | KH | KM | NewEvent,
handler: (
event:
| WindowEventMap[KW]
| HTMLElementEventMap[KH]
| MediaQueryListEventMap[KM]
| Event,
) => void,
element?: RefObject<T>,
options?: boolean | AddEventListenerOptions,
) {
// Create a ref that stores handler
const savedHandler = useRef(handler);
useIsomorphicLayoutEffect(() => {
savedHandler.current = handler;
}, [handler]);
useEffect(() => {
// Define the listening target
const targetElement: T | Window = element?.current ?? window;
// @ts-ignore
if (!targetElement?.addEventListener) return;
// Create event listener that calls handler function stored in ref
const listener: typeof handler = (event) => savedHandler.current(event);
// @ts-ignore
targetElement.addEventListener(eventName, listener, options);
// Remove event listener on cleanup
return () => {
// @ts-ignore
targetElement.removeEventListener(eventName, listener, options);
};
}, [eventName, element, options]);
}
export { useEventListener };
import { useEffect, useLayoutEffect } from "react";
export const useIsomorphicLayoutEffect =
typeof window !== "undefined" ? useLayoutEffect : useEffect;
import { useState } from "react";
import { useEventListener } from "./useEventListener";
export function useUserIteration(callback?: () => void) {
const [userHasInteracted, setUserHasInteracted] = useState(false);
const setTrue = () => {
if (userHasInteracted) return
setUserHasInteracted(true);
if (callback) {
callback();
}
};
const options = {
once: true,
};
useEventListener("mousemove", setTrue, undefined, options);
useEventListener("mousedown", setTrue, undefined, options);
useEventListener("resize", setTrue, undefined, options);
useEventListener("keydown", setTrue, undefined, options);
useEventListener("touchstart", setTrue, undefined, options);
useEventListener("wheel", setTrue, undefined, options);
return { userHasInteracted };
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment