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 🙏

© 2024 – Pkg Stats / Ryan Hefner

resonant

v1.2.0

Published

[![Coverage Status](https://coveralls.io/repos/github/MatthewZito/resonant/badge.svg?branch=master)](https://coveralls.io/github/MatthewZito/resonant?branch=master) [![Continuous Deployment](https://github.com/MatthewZito/resonant/actions/workflows/cd.yml

Downloads

5

Readme

resonant

Coverage Status Continuous Deployment Continuous Integration npm version License: MIT

Reactive effects with automatic dependency management, caching, and leak-free finalization.

Table of Contents

Installation

npm install resonant

Supported Environments

resonant currently supports UMD, CommonJS (node versions >= 10), and ESM build-targets

Commonjs:

const { resonant, effect } = require('resonant');

ESM:

import { resonant, effect } from 'resonant';

Documentation

Inspired by React's useEffect and Vue's watchEffect, resonant is a compact utility library that mitigates the overhead of managing observable data, such as dependency tracking; caching and cache invalidation; and object dereferencing and finalization.

In resonant, an effect is a computation that is automatically invoked any time its reactive state changes. An effect's reactive state is any state that is accessed inside the effect body (specifically, the function passed to the effect initializer). A deterministic heuristic follows that any data access that triggers getters will be visible to and therefore tracked by the effect.

This reactive state 'resonates', hence resonant.

Creating an Effect

To create an effect, you must first make the target object (the effect state) reactive with the resonant function:

import { resonant } from 'resonant';

const plainObject = {
  x: 1,
  y: 1
};

const r = resonant(plainObject);

r is now equipped with deep reactivity. All getters / setters will trigger any effects that happen to be observing the data.

Let's create an effect:

import { resonant, effect } from 'resonant';

const plainObject = {
  x: 1,
  y: 1
};

const r = resonant(plainObject);

let count = 0;

effect(() => {
  count += r.x + r.y;
});

The effect will be invoked immediately. Next, the effect is cached and tracks r as a reactive dependency. Any time r.x or r.y is mutated, the effect will run.

This paradigm works with branching and nested conditionals; if the effect encounters new properties by way of conditional logic, it tracks them as dependencies.

const r = resonant({
  x: {
    y: {
      z: 1
    }
  }
});

effect(() => {
  if (r.x.y.k) {
    if (r.x.y.z > 1) {
      computation();
    }
  }
});

// the outer condition evaluates to `false`
// the effect doesn't need to know about `r.x.y.z` yet - we'll track it only when necessary

r.x.y.k = 1;
// now, the outer condition evaluates to `true`
// the effect will see the second condition and begin tracking `r.x.y.z`

Effect dependencies are tracked lazily; the effect only ever cares about resonant data that it can see.

resonant uses weak references; deleted properties to which there are no references will be finalized so they may be garbage collected, as will all of that property's dependencies and effects.

Starting and Stopping an Effect

To control an effect, each effect initializer returns unique stop, start, and toggle handlers. These functions are used to pause, resume, or toggle the effect's active state.

Use stop to pause an effect. The effect will not run during this period. Stopping an effect flushes its dependency cache, so subsequent start or toggle calls are akin to creating the effect anew.

import { resonant, effect } from 'resonant';

const r = resonant({ x: 1 });

let c = 0;
const { stop } = effect(() => {
  c += r.x;
});

// initial run - `c` == 1

r.x++;

// trigger - `c` == 3

stop();

r.x++;

// `c` == 3

Use start to transition the effect to an active state. start is idempotent; if the effect is already active, invoking start will not immediately trigger the effect. Otherwise, start - like instantiating a new effect - will run the effect immediately.

import { resonant, effect } from 'resonant';

const r = resonant({ x: 1 });
let c = 0;

const { stop, start } = effect(() => {
  c += r.x;
});
// initial run - r.x == 1, c == 1

r.x++;
// r.x == 2, c == 3

stop();

r.x++;
// r.x == 3, c == 3

start();
// initial run - r.x == 3, c == 6

r.x++;
// r.x == 4, c == 7

Use toggle to toggle the effect's active state. Toggle invokes the appropriate start or stop handler and returns a boolean indicating whether the effect's state is active.

import { resonant, effect } from 'resonant';

const r = resonant({ x: 1 });
let c = 0;
let isActive = true;

const { toggle } = effect(() => {
  c += r.x;
});
// initial run - r.x == 1, c == 1

r.x++;
// r.x == 2, c == 3

isActive = toggle();
// isActive == false

r.x++;
// r.x == 3, c == 3

isActive = toggle();
// isActive == true
// initial run - r.x == 3, c == 6

r.x++;
// r.x == 4, c == 7

Deferred Effects

Effects may be initialized lazily with the lazy option. Passing this optional flag to the effect initializer will initialize the effect in an inactive state. The effect will not run immediately; either the effect's start or toggle handler must be invoked before the effect can trigger.

import { resonant, effect } from 'resonant';

const r = resonant({ x: 1 });
let c = 0;

const { start } = effect(() => {
  c += r.x;
}, { lazy: true });
// no initial run - r.x == 1, c == 0

r.x++;
// r.x == 2, c == 0

start();

r.x++;
// r.x == 3, c == 3

Full documentation and type signatures can be found here