Skip to content
This repository was archived by the owner on Oct 14, 2025. It is now read-only.

A Simple wrapper for `EventTarget#addEventListener` returning a disposer.

Notifications You must be signed in to change notification settings

ShaMan123/easyAddEventListener

Repository files navigation

The Easiest

Using AbortController is the best approach and requires nothing additional. Therefore this repo will be archived.

const abortController = new AbortController();
window.addEventListener("load", () => {}, abortController);

abortController.abort();

[ARCHIVED] Easy addEventListener

Test

A Simple wrapper for EventTarget#addEventListener returning a disposer.

Inspired by react's useEffect.

Motivation

const listener = (e: PointerEvent) => {};
window.addEventListener("pointerdown", listener);

window.removeEventListener("pointerdown", listener);

This pattern is not great DX in real life usage, especially when addEventListener and removeEventListener are not called within the same scope (quite common).

// Weird that listener is defined here, far away from the state it depends on
// probably causing more code coupling and weird patterns
const listener = (e: PointerEvent) => {};

function App() {
  // ...
  function deepState() {
    window.addEventListener("pointerdown", listener);
  }
}

function cleanup() {
  window.removeEventListener("pointerdown", listener);
}

Thinking of it, listener is (obviously) coupled to addEventListener.

It is used as an id by removeEventListener (to perform the removal), resulting in coupling.
Returning a disposer decouples this relation.

const disposers: VoidFunction[] = [];

function App() {
  // ...
  function deepState() {
    disposers.push(window.addEventListener("pointerdown", (e) => {}));
  }
}

function cleanup() {
  disposers.splice(0, disposers.length).forEach((d) => d());
}

This pattern saves the need to declare listeners just for the sake of being able to call removeEventListener.
It allows to inline the code resulting in better code structure.

And it is EASY!

// React

function App() {
  useEffect(
    () =>
      window.addEventListener("pointerdown", (e) => {
        console.log(dep);
      }),
    [dep]
  );
}

Quick Start

npm i -S easy-add-event-listener

Basic Usage

import addEventListener from "easy-add-event-listener";

const disposer = addEventListener(
  document,
  "wheel",
  (e) => e satisfies WheelEvent
);

disposer();

Shimming

import "easy-add-event-listener/shim"; // shims globals and TS declarations

const disposer = window.addEventListener("load", () => {});

disposer();

I love it! ⭐

Shimming Custom Types

  • Install dev deps
  • Run cli.js to generate shimmed types
  • Import the types into your project
  • In order to patch your source files use the --patch option followed by patch-package

Advanced Usage

// Setup

import addEventListener from "easy-add-event-listener";

interface MyEventTargetEvents {
  foo: PointerEvent;
  bar: CustomEvent;
}

class MyEventTarget extends EventTarget {}

const easy = addEventListenerFactory<MyEventTarget, MyEventTargetEvents>();

// Usage

const instance = new MyEventTarget();

const disposers = [
  easy(instance, "foo", (e) => e satisfies PointerEvent),
  easy(instance, "bar", (e) => e satisfies CustomEvent),
];

disposers.splice(0, disposers.length).forEach((d) => d());

CI

Generate Types

Typing the function was the real effort of this repo

npm run parse -- -h

Publish

No need to build.

npm version
npm publish

About

A Simple wrapper for `EventTarget#addEventListener` returning a disposer.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

 

Packages

No packages published