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

@ryanmorr/define-store

v1.0.1

Published

Define custom stores for reactive programming

Downloads

71

Readme

define-store

Version Badge License Build Status

Define custom stores for reactive programming

Install

Download the CJS, ESM, UMD versions or install via NPM:

npm install @ryanmorr/define-store

Usage

Easily create customizable observable stores that allow you control every aspect of it's behavior and API, including how it's created, how and when it's internal value is read and written, and how subscribers are handled. This allows you to create stores with specific functionality and still maintain interoperability with different reactive libraries.

import defineStore from '@ryanmorr/define-store';

// Define a store with a callback function that is provided a function to get the
// internal value, a function to set the internal value, a function to add
// subscribers, and the subscribers array as the 4 parameters
const store = defineStore((get, set, subscribe, subscribers) => {

    // Return a function for creating store instances with custom arguments
    return (initialValue) => {

        // Set the initial value upon creation
        set(initialValue);

        // This return object exposes the external API for interacting
        // with a store instance
        return {

            // Add `get` method
            get,

            // Add `set` method
            set(...args) {

                // The `set` function supports multiple arguments that will all be
                // passed to the subscribers when called, only the first argument
                // will be set to the new internal value
                set(...args);
            },

            // By default, the `subscribe` function is automatically added to the
            // return object unless it is explicitly overridden
            subscribe(callback) {

                // The `subscribe` function returns an `unsubscribe` function for the
                // subscriber when called
                const unsubscribe = subscribe(callback);

                // Override `unsubscribe` and return it as a method of an object instead
                // of a function
                return {
                    unsubscribe() {
                        unsubscribe();

                        // The `subscribers` array is live, it's useful for executing
                        // code when the first subscriber is added or the last is removed
                        if (subscribers.length === 0) {
                            
                        }
                    }
                };
            }
        };
    };
});

// Create an instance of the store with an initial value
const value = store(100);

// Add a subscriber
const subscriber = value.subscribe((newValue) => console.log(newValue));

// Get the stored value
value.get(); //=> 100
// Set the stored value
value.set(200);
// Get the new stored value
value.get(); //=> 200

// Remove subscriber
subscriber.unsubscribe();

// Supports implicit type conversions
value.toString(); //=> "200"
value.valueOf(); //=> 200
value.toJSON(); //=> 200
await value; //=> 200

Examples

You can make all kinds of different observable stores, for example, here's a basic function-based store:

const store = defineStore((get, set) => (value) => {
    set(value);
    return (...args) => {
        if (args.length === 1) {
            set(args[0]);
        }
        return get();
    };
});

const value = store('foo');

const subscribe = value.subscribe((val) => console.log(val));

value(); //=> "foo"
value('bar');
value(); //=> "bar"

And how about a computed store to go with it:

const computed = defineStore((get, set) => (deps, callback) => {
    const setValue = () => set(callback(deps.map((dep) => dep())));
    deps.forEach((dep) => dep.subscribe(setValue));
    setValue();
    return get;
});

const firstName = store('John');
const lastName = store('Doe');
const fullName = computed([firstName, lastName], ([first, last]) => `${first} ${last}`);

const subscribe = fullName.subscribe((name) => console.log(name));

fullName(); //=> "John Doe"
firstName('Jane');
fullName(); //=> "Jane Doe"
lastName('Smith');
fullName(); //=> "Jane Smith"

Or maybe a simple toggle store:

const toggle = defineStore((get, set) => (on = false) => {
    set(on);
    return {
        isOn: get,
        on: () => set(true),
        off: () => set(false),
        toggle: () => set(!get())
    };
});

const toggler = toggle();

const subscribe = toggler.subscribe((on) => console.log(on));

toggler.on();
toggler.isOn(); //=> true
toggler.off();
toggler.isOn(); //=> false
toggler.toggle();
toggler.isOn(); //=> true

Don't forget your Redux-style reducer store:

const reduce = defineStore((get, set) => (reducer, initialState) => {
    set(initialState);
    return {
        getState: get,
        dispatch: (action) => set(reducer(get(), action))
    };
});

function reducer(state, action) {
    switch (action.type) {
        case 'increment':
            return {count: state.count + 1};
        case 'decrement':
            return {count: state.count - 1};
        default:
            return state;
    }
}

const state =  {count: 0};
const counter = reduce(reducer, state);

const subscribe = counter.subscribe((state) => console.log(state));

counter.dispatch({type: 'increment'});
counter.getState(); //=> {count: 1}
counter.dispatch({type: 'decrement'});
counter.getState(); //=> {count: 0}

License

This project is dedicated to the public domain as described by the Unlicense.