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

@fluentkit/observable

v1.0.4

Published

A lightweight object proxy for reactivity with dependency tracking, watchers, effects and cached object getters.

Downloads

10

Readme

@fluentkit/observable

NPM Downloads Bundlephobia Issues License NPM Downloads jsDelivr hits (npm) Unpkg

A lightweight 3KB (minified), 1KB gzipped, zero dependency* object proxy for reactivity with dependency tracking, watchers, effects and cached object getters.

Inspired by VueJs Reactivity.

Watchers and effects are batched, de-duped and called asynchronously using promises for performance.

* Requires the javascript Promise function, and the Reflect api. Internet Explorer is NOT supported.

Usage

To create a reactive object, import the observable function and provide your initial object as its only argument.

import {observable} from '@fluentkit/observable';

const reactiveObj = observable({
    foo: 'bar',
    bazzer: {
        one: 'two',
        three: ['four', 'five', 'six']
    },
    get computedValue() {
        return this.bazzer.one + ',' + this.bazzer.three.join(',');
    }
});

CDN Usage

You can use a prebuilt copy of the package from the sources below:

https://unpkg.com/@fluentkit/observable

https://cdn.jsdelivr.net/npm/@fluentkit/observable

In both case the observable function will be available on the global variable FluentKit:

const obj = FluentKit.observable({});

API

$watch: (PropertyKey | PropertyKey[] | Function, callback?: Function): void

To watch for data changes you can use the watch function:

// when you provide just one argument the watcher is called on all changes:
reactiveObj.$watch((propertyName) => {
    // here propertyName equals the (possibly nested) object key that was changed.
});

// or indicate string property to watch:
reactiveObj.$watch('foo', () => {
    // reactiveObj.foo changed.
});

// and finally watch multiple properties with one callback:
reactiveObj.$watch(['foo', 'bazzer.one'], (propertyName) => {
    // propertyName changed.
});

$watchSync: (PropertyKey | PropertyKey[] | Function, callback?: Function): void

Provides the same api as $watch but runs the callback function immediately. This method is used internally to clear cached computed values, it is exposed but most of your needs should be covered by $watch.

$effect: (callback: () => {}): void

Effect when called first evaluates the supplied callback, tracking any dependencies accessed. Then when those dependencies change the callback is re-run, re-tracking the dependencies.

reactiveObject.$effect(() => {
    console.log('effect called!', 'foo is:', reactiveObj.foo, 'bazzer is:', reactiveObj.bazzer);
});

// >> effect called .....

reactiveObject.bazzer.one = 'three'
// >> effect called .....

reactiveObj.newProperty = 'foobarbazzer'

// >> effect NOT called

$track: (callback: () => {}): string[]

Mainly used internally the $track method returns the property keys accessed during the evaluation of its supplied callback.

const dependencies = reactivObj.$track(() => {
    const foo = reactiveObj.foo;
    const bazzer = reactiveObj.bazzer;
});

// dependencies = ['foo', 'bazzer'];

$nextTick: (callback?: () => {}): Promise

Allows you to run actions after any watchers and effects have been applied for the current observables modifications.

$nextTick returns a promise, so you can provide a then callback, or await $nextTick in async functions.

reactiveObj.foo = 'zab';
// modifications here runs before any watchers/effects on `foo`
await reactiveObj.$nextTick();
// modifications here run AFTER watchers and effects for `foo`

$isSettled: boolean

Indicates if all watchers and effects have been run, and no new items have been passed to the queue.

$isObservable: boolean

Indicates an object is already an observable.

Computed values

Borrowed from Vue, "computed" values are just native object getters which can be defined upon creation:

const obj = observable({
    foo: 'bar',
    get computed() {
        return this.foo.split('').reverse().join('');
    }
});

Or added later using Object.defineProperty:

Object.defineProperty(obj, 'computed', {
    get () {
        return this.foo.split('').reverse().join('');
    }
});

Getters or "computed" properties are great for intensive operations. What's more when accessed their values are cached and returned without re-invoking until one of their dependencies change:

const obj = observable({
    foo: 'bar',
    bazzer: 'rezzab',
    get computed() {
        console.log('called');
        return this.foo.split('').reverse().join('');
    }
});

let computed = obj.computed; // === rab
// >> called
computed = obj.computed; // === rab
obj.bazzer = 'bazzer';
computed = obj.computed; // === rab
computed = obj.computed; // === rab
computed = obj.computed; // === rab
// >> NOT called
obj.foo = 'rab';
computed = obj.computed; // === bar
// >> called
computed = obj.computed; // === bar
computed = obj.computed; // === bar
computed = obj.computed; // === bar
// >> NOT called

Nested Observables

Observables can be nested and watched, to watch the whole child object:

obj.child = observable({ foo: 'bar' });

obj.$watch('child', () => {
    // child, was reassigned, deleted, or its internal values changed.
});

obj.$watch('child.foo', () => {
    // child.foo, was reassigned, deleted, or its value changed.
});