side-effect-manager

A tiny library to encapsulate side effects in a compact, reusable and testable style.

Usage no npm install needed!

<script type="module">
  import sideEffectManager from 'https://cdn.skypack.dev/side-effect-manager';
</script>

README

side-effect-manager

Build Status npm-version Coverage Status

Commitizen friendly Conventional Commits code style: prettier

A tiny library to encapsulate side effects in a compact, reusable and testable style.

Install

npm add side-effect-manager

Why

Conventionally we write side effects like this:

class MyClass {
  constructor() {
    this.handleResize = () => {
      console.log("resize");
    };
    window.addEventListener("resize", this.handleResize);
  }

  destroy() {
    // cleanup side effects
    window.removeEventListener("resize", this.handleResize);
  }
}

This code style is scattered and hard-to-follow. The side effect handler has to be exposed to this which leaves us many unwanted and uncompressible properties.

With side-effect-manager we may write the same logic like this instead:

import { SideEffectManager } from "side-effect-manager";

class MyClass {
  constructor() {
    this.sideEffect = new SideEffectManager();

    this.sideEffect.add(() => {
      const handleResize = () => {
        console.log("resize");
      };
      window.addEventListener("resize", handleResize);
      return () => window.removeEventListener("resize", handleResize);
    });

    // or simply like this
    this.sideEffect.addEventListener(window, "resize", () => {
      console.log("resize");
    });
  }

  destroy() {
    this.sideEffect.flushAll();
  }
}

Not only the code is more compact and readable, variables can now be compressed as they are not properties.

Usage

Add a side effect:

sideEffect.add(() => {
  const subscription = observable$.subscribe(value => {
    console.log(value);
  });
  return () => subscription.unsubscribe();
});

There are also sugars for addEventListener, setTimeout and setInterval.

sideEffect.setTimeout(() => {
  console.log("timeout");
}, 2000);

Adding a side effect returns a disposerID which can be used to remove or flush a side effect.

const disposerID = sideEffect.addEventListener(window, "resize", () => {
  console.log("resize");
});

// Remove the side effect without running the disposer callback
sideEffect.remove(disposerID);

// Remove the side effect then run the disposer callback
sideEffect.flush(disposerID);

A disposerID can also be set deliberately. Side effects with the same ID will be flushed before adding a new one.

function debounce(handler, timeout) {
  sideEffect.setTimeout(handler, timeout, "my-timeout");
}