Neat! Pickled Muskrat!

    side-effect-manager
    TypeScript icon, indicating that this package has built-in type declarations

    1.2.1 • Public • Published

    side-effect-manager

    Build Status npm-version Coverage Status minified-size tree-shakable

    Commitizen friendly Conventional Commits code style: prettier

    A tiny library to encapsulate side effects in a compact, reusable, testable and TypeScript-friendly 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);
        });
      }
    
      destroy() {
        this.sideEffect.flushAll();
      }
    }

    Or simply like this:

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

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

    Also the typing of listener can now be inferred from method so it is also more TypeScript friendly without the need to declare listener type specifically.

    Usage

    Add a side effect:

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

    If the side effect returns a disposer function directly you can also:

    import Emittery from "emittery";
    import { Remitter } from "remitter";
    
    const emittery = new Emittery();
    const remitter = new Remitter();
    
    sideEffect.addDisposer(
      remitter.on("event1", eventData => console.log(eventData))
    );
    
    // Or an array of disposers
    sideEffect.addDisposer([
      remitter.on("event1", eventData => console.log(eventData)),
      remitter.on("event2", eventData => console.log(eventData)),
      emittery.on("event3", eventData => console.log(eventData)),
    ]);

    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");
    }

    Async Side Effects

    Similar to SideEffectManager, AsyncSideEffectManager can also handle async side effect cleanup nicely.

    import { AsyncSideEffectManager } from "side-effect-manager";
    
    const asyncSideEffect = new AsyncSideEffectManager();
    
    asyncSideEffect.add(async () => {
      // async side effect
    
      return async () => {
        // async cleanup
      };
    });

    Yon can add or flush side effects with the same ID repeatably. AsyncSideEffectManager will correctly schedule tasks and skip unnecessary tasks automatically.

    const disposerID = asyncSideEffect.add(async () => {
      // async side effect
    
      return async () => {
        // async cleanup
      };
    });
    
    // Add side effect with same ID
    asyncSideEffect.add(async () => {
      // async side effect
    
      return async () => {
        // async cleanup
      };
    }, disposerID);
    
    asyncSideEffect.flush(disposerID);

    You can always await asyncSideEffect.finished which will be updated and resolved after all tasks are finished.

    await asyncSideEffect.finished;

    Helpers

    • joinDisposers Join multiple disposers into on disposer
    • joinAsyncDisposers Join multiple disposers into on disposer and wait until all disposers are resolved.

    Keywords

    none

    Install

    npm i side-effect-manager

    DownloadsWeekly Downloads

    386

    Version

    1.2.1

    License

    MIT

    Unpacked Size

    81.5 kB

    Total Files

    19

    Last publish

    Collaborators

    • straybugs