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

abon

v0.6.2

Published

Flexible state management for React 🚀

Downloads

13

Readme

Stable Release Blazing Fast gzip size license


Did you ever get tired of the lack of type-safeness or superflous code when using Redux? Or the decorator weirdness using MobX? Then you should npm install abon.

Now, using Abon requires a different approach to state. MobX and Redux, for example, requires you to build your state in a very specific way, and to subscribe to it in a very specific way as well. In Abon's point of view, a value can be stored anywhere, and be used anywhere—inside or outside of components. It can be passed down to components without requiring them to be subscribed to the values, or used inside components without forcing a re-render when updated. That is the immense flexibility that Abon allows for.

To learn more about how Abon can be used, you can check out some of our examples.

Abon

Allows for holding, setting and subscribing to a single value. Subscribers are only notified when the set value differs from the current, or if the notify method is used to manually notify subscribers.

import { Abon } from "abon";

const authenticated = new Abon<boolean>(false);

function AuthMessage() {
    const auth = authenticated.use().current;

    if (!auth) {
        return <p>No access</p>;
    }

    return <p>Welcome</p>;
}

AbonDeep

Allows for holding, setting, getting and subscribing to nested values. Subscribers are only notified when the set value differs from the current value (given the keys or lack of keys), or if the notify method is used to manually notify subscribers.

import { AbonDeep } from "abon";

const value = new AbonDeep<{ a: { b: { c: number } } }>({ a: { b: { c: 0 } } });

value.subscribe(
    ["a", "b", "c"],
    (c) => { /** handle a.b.c */ })
);

value.subscribe(
    "a",
    (a) => { /** handle a */  })
);

value.set({
    a: { b: { c: 1 } }
});

value.set("a", "b", "c", 2);

AbonSet

Extends Set with additional features allowing for setting and modifying values in one sweep. Whenever a value is added or deleted, subscribers are notified. They can also be notified manually using the notify method.

import { AbonSet } from "abon";

const numbers = new AbonSet<number>([1, 2]);

function DisplayNumbers() {
    const numbersAmount = numbers.use().size

    if (numbersAmount > 0) {
        return <p>No numbers</p>;
    }

    return <p>Numbers: {numbersAmount}</p>;
}

AbonMap

Extends Map with additional features allowing for setting and modifying values in one sweep. Whenever a value is added, modified or deleted, subscribers are notified. They can also be notified manually using the notify method, which accepts a set of keys if only specific keys should be notified.

import { AbonMap } from "abon";

const userFollowers = new AbonMap<string, number>([["james", 10]]);
// [["james", 10]]
userFollowers.delete("james");
// []
userFollowers.set("dude", 12);
// [["dude", 12]]
userFollowers.set({
    james: 8,
})
// [["james", 8]]
userFollowers.set({
    james: 6,
    dude: 10
})
// [["james", 6], ["dude", 10]]
userFollowers.patch({
    dude: 12
})
// [["james", 6], ["dude", 12]]
function UserFollowers(props: { userName: string }) {
    const followers = userFollowers.use(props.userName);

    return <p>Followers: {followers || 0}</p>;
}

function TotalFollowers() {
    const totalFollowers = Array.from(userFollowers.use().entries()).reduce(
        (totalFollowers, [userName, followers]) => totalFollowers + followers,
        0,
    );

    return <p>Total followers: {totalFollowers || 0}</p>;
}

AbonArray

A subscribable implementation of a normal array, with some additional methods.

import { AbonArray } from "abon";

const indexes = new AbonArray<number>();
indexes.push(2, 4, 5, 9);
const index = indexes.find(index => index > 5); // 9

AbonItems

Allows for dynamically setting, pushing, unshifting, and subscribing to property-identified values.

import { AbonItems } from "abon";

const items = new AbonItems<{ itemId: number; name: string; }, "itemId">("itemId", [{ itemId: 5, name: "Jason" }]);

items.push({ itemId: 2, name: "Luke" })
// items.ids.current: [5, 2]
items.unshift({ itemId: 3, name: "Marie" })
// items.ids.current: [3, 5, 2]
items.set([{ itemId: 7, name: "Mason" }, { itemId: 1, name: "Chlorine" }])
// items.ids.current: [7, 1]
items.delete(7)
// items.ids.current: [1]
// items.items: [{ itemId: 1, name: "Chlorine" }]
// items.current: { 1: { itemId: 1, name: "Chlorine" } }
items.set(5, "name", "Jake")
// does not make difference because an item where `itemId === 5` does not exist
items.set(7, "name", "Charlotte")
// items.items: [{ itemId: 7, name: "Charlotte" }]

items.subscribe((current, items, ids) => {
    // items equal ids.map(id => current[id])
})

AbonAsync

Allows for cancelling/overriding the process of setting values that are retrieved asynchronously by providing them as promises. For that purpose, the set methods accepts a promise whose value is to be set once it is resolved unless a different value is set during that time. Additionally, the dispatch method allows for handling promises unrelated to the value, meaning the passed handler is called if no new value is set during the time of the resolution of the passed promise.

import { AbonAsync } from "abon";

const name = new AbonAsync<string>();

const namePromises = [
    new Promise((resolve) => setTimeout(() => resolve("Hjalmar"), 100)),
    new Promise((resolve) => setTimeout(() => resolve("Sören"), 400)),
    new Promise((resolve) => setTimeout(() => resolve("Rory"), 300)),
];

name.set(namePromises[3] /** Promise<"Rory"> */);
name.promise.then((currentName) => { /** "Sören", since it is set last. */ });
name.dispatch(Promise.resolve(), () => { /** Never called, because the next line is set before `Promise.resolve()` has been resolved. */ })
name.set(namePromises[2] /** Promise<"Sören"> */);

name.promise.then((currentName) => { /** "Sören", too. */ 
    name.set(namePromises[0] /** Promise<"Hjalmar"> */);
    name.promise.then((currentName) => { /** "Joker", since it is set last. */ });
    name.set("Joker");
});

AbonInheritedDown

Imagine a tree of values, where all branches and sub-branches inherit from their parent, unless they have a value defined themselves. This is useful, for example, for having nodes that can inherit a status from their parents, such as a loading status for form fields in a form.

import { AbonInheritedDown } from "abon";

const value = new AbonInheritedDown<number>(1);
const subValue = value.nest()
const subSubValue = subValue.nest()

value.current       // 1, from itself
subSubValue.current // 1, from `value`
subValue.set(2);
subSubValue.current // 2, from `subValue`
subValue.current    // 2, from itself
subSubValue.set(3);
subSubValue.current // 3, from itself
subSubValue.set(undefined);
subSubValue.current // 2, from `subValue`
subValue.set(undefined);
subSubValue.current // 1, from `value`
value.set(undefined)
subSubValue.current // undefined
subValue.current    // undefined
value.current       // undefined

AbonInheritedUp

Every branch looks to itself or to any of its children (added using the addChild method) for a defined value.

import { AbonInheritedDown } from "abon";

const name = new AbonInheritedUp<string>();
const subName = name.addChild();
const subSubName = subName.addChild()

name.current       // undefined
subSubName.set("Mike");
name.current       // "Mike", from `subSubName`
subName.current    // "Mike", from `subSubName`
subSubName.current // "Mike", from itself
subName.set("Oprah");
name.current       // "Oprah", from `subName`
subName.current    // "Oprah", from itself
subSubName.current // "Mike", from itself
subSubName.set(undefined);
name.current       // "Oprah", from `subName`
subName.current    // "Oprah", from itself
subSubName.current // undefined
subName.set(undefined);
name.current       // undefined
subName.current    // undefined
subSubName.current // undefined

Utility

The Abon class provides some static utility for composing and constructing the instances described above.

Abon.useComposedValue

Create an Abon with a value based on one or more subscriptions. Dependencies can be passed as a third argument, which means the value will be recalculated if either (1) the listener is notified or (2) the dependencies have changed. Changing the dependencies also re-subscribes (meaning you can change the subscriptions).

There is also Abon.useComposedValueAsync, which allows for returning a promise from the getter function, meaning the value returned from the hook will be undefined at first.

import { Abon, AbonDeep } from "abon";

function PriceStatus() {
    const price = Abon.useRef<number>(() => 10);
    const fullPrice = Abon.useRef<number>(() => 100);

    // `0.1` initially, then updates whenever `price` or `fullPrice` change
    const priceFraction = Abon.useComposedValue(
        () => price.current / fullPrice.current,
        (listener) => [fullPrice.subscribe(listener), price.subscribe(listener)],
        [price, fullPrice]
    );

    if (priceFraction < 0.1) {
        return <p>Good price!</p>;
    }

    return <p>Bad price.</p>;
}

Abon.resolve

Returns a promise resolved by the next value set to the provided Abon. You can also pass a function accepting a listener and returning a created subscription, which will resolve the promise with the value that is passed to the listener.

import { Abon } from "abon";

const value = new Abon(0);

Abon.resolve(value).then((_value) => { /** 1 */ })
value.set(1);

Authors


MIT License.