npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

use-eventer

v1.2.5

Published

A custom React hook for adding event listeners to one or multiple elements with flexible options.

Readme

use-eventer

A custom React hook for adding event listeners to one or multiple elements with flexible options.

Index

Features

  • Supports single or multiple refs and event types.
  • Handles one-to-one (element-to-event) or one-to-many mappings.
  • Provides options for immediate handler execution and event listener configurations.
  • Automatically cleans up listeners on unmount or dependency change.

Installation

This is a standalone hook. You can simply copy and use it from this gist in your React project.

Or you can install it using npm:

npm i use-eventer

Usage

  • It's simply a useEffect that runs your code with dependencies passed.

  • You can directly include all the logic you would typically use inside a useEffect hook, but instead, return the event handler function.

  • No need for cleanup code.

Basic Example

import { useRef } from "react";
import useEventer from "./useEventer";

const MyComponent = () => {
    const buttonRef = useRef(null);

    useEventer(buttonRef, "click", () => () => {
        console.log("Button clicked!");
    }, []);

    return <button ref={buttonRef}>Click Me</button>;
};

Advanced Showcases

This hook shines when handling multiple elements, multiple events, or both, all with a shared event handler.

Also, It saves alot of cleanup code.

Single Event and Element

Here, it simply saved the cleanup code.

const divRef = useRef(null);

useEventer(divRef, "click", () => (event) => {
    console.log(`Event triggered: ${event?.type}`);
}, []);

Multiple Events on a Single Element

const divRef = useRef(null);

useEventer(divRef, ["mouseenter", "mouseleave"], () => (event) => {
    console.log(`Event triggered: ${event?.type}`);
}, []);

Multiple Elements and Events

Before using useEventer, you might handle multiple elements and events like this:

const div1 = useRef(null);
const div2 = useRef(null);

useEffect(() => {
    const eventHandler = () => console.log("Event triggered!");

    const controller = new AbortController();
    
    const element1 = div1.current;
    const element2 = div2.current;

    element1?.addEventListener("click", eventHandler, controller);
    element1?.addEventListener("mouseenter", eventHandler, controller);

    element2?.addEventListener("click", eventHandler, controller);
    element2?.addEventListener("mouseenter", eventHandler, controller);

    return () => {
        controller.abort();
    };
}, []);

With useEventer, this can be simplified:

const div1 = useRef(null);
const div2 = useRef(null);

useEventer([div1, div2], ["click", "mouseenter"], () => () => {
    console.log("Event triggered!");
}, []);

By default, oneToOne is set to false, meaning each div will have both event listeners attached. If oneToOne is set to true, each div will only have the event listener corresponding to its order.

Specific Case

Sometimes, you may need to add some logic (and maybe with dependencies) before event attachment. You can add it in the callback passed, and then return the event handler.

Before Using useEventer

const divRef = useRef(null);

useEffect(() => {
    console.log("color changed");

    const eventHandler = (event) => {
        console.log(`color is: ${color}`);
    };

    const element = divRef.current;
    element?.addEventListener("click", eventHandler);

    return () => {
        element?.removeEventListener("click", eventHandler);
    };
}, [color]);

With useEventer

const divRef = useRef(null);

useEventer(divRef, "click", () => {
    console.log("color changed");

    return (event) => {
        console.log(`color is: ${color}`);
    };
}, [color]);

Here, Both work exactly the same.

useEventer is simply a useEffect, but you can add your event listeners with shared dependencies to save lines of code.

Cleanup Behavior

  • The hook automatically removes event listeners when the component unmounts.
  • If dependencies change, listeners are re-registered accordingly.
  • The AbortController is used for efficient cleanup.
  • You can pass a signal to the event listener for your custom use, but in this case, cleanup function will not use the AbortController. Instead, it will manually remove all listeners.

Error Handling

Throws an error if oneToOne is true but the number of refs and events don't match.

API

useEventer(ref, event, callback, dependencies?, options?)

| Parameter | Type | Description | | -------------- | ----------------------------------------------- | ----------- | | ref | RefObject<T> \| RefObject<T>[] | A single or array of refs pointing to elements. | | event | string \| string[] | The event type(s) to listen for. | | callback | () => EventListenerCallback | A function returning the event handler. | | dependencies | any[] (optional, default: []) | Dependencies for useEffect. | | options | HookOptions (optional, default: {}) | Additional configuration options. |

HookOptions

| Option | Type | Default | Description | | ------------------ | ----------------------- | ------- | ----------- | | oneToOne | boolean | false | If true, each event applies to a single corresponding element. Otherwise, all events apply to all elements. | | callHandlerOnce | boolean | false | If true, the handler is called once immediately after setup. | | callHandlerOnEach | boolean | false | If true, the handler is called on each event binding. | | listenerOptions | AddEventListenerOptions | {} | Additional options for the event listener. |

Notes

  • The callback must return an event listener function.
  • The listenerOptions allow passing options like passive, capture, or once.

This hook provides a simple and flexible way to manage event listeners in React. 🚀