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

simple-data-store

v3.3.1

Published

A simple data store for state management.

Downloads

297

Readme

Simple Data Store

NPMBadge for Gzip size

A Typescript based simple data store for state management.

My alternative to redux and other state management.

Install

To get from npm simply run.

npm install --save simple-data-store

Alternatively you can download the code and build it yourself with

npm run build

And in the dist folder will be the Javascript and Typescript typings file.

Also the whole thing is one Typescript file so it's pretty easy to manually add it to your own source code.

Features

  • Small file size (about 0.5kb after compression)
  • Immutable.
  • Simple API.
  • Has support for history, with an example implementation found here
  • Selector support (along the lines of reselect for redux).
  • No dependencies

Why?

Do we ever need more JS/TS libraries?

I like what redux and other functional state management libraries have done for UI but I found them too unstructured and too generalised. So I put together my own version that is still general but puts just enough structure to fulfil my needs.

Example

Modifiers are functions that perform an action on the state. This means that the state does not need to know about modifiers at all.

In the example below it is shown that creating a function that returns the modifier with the values in the closure is useful.

import DataStore from "../src";

interface SimpleState
{
    readonly counter: number;
}

function change(value: number)
{
    return (state: SimpleState) => ({ counter: state.counter + value });
}

const store = new DataStore<SimpleState>
({
    counter: 0
});

store.subscribeAny((state) => console.log(state));

store.execute(change(1));
store.execute(change(2));
store.execute(change(-1));

/* Example output:
{ counter: 1 }
{ counter: 3 }
{ counter: 2 }
*/

There is a longer example at example/sample.ts.

API

Types

// A function to create a patch for the state.
// Meaning this should return a partial state to be merged with the current state.
export type Modifier<TState> = (state: TState) => Partial<TState> | null;

// A function that takes part of the state and returns a sub set of that state.
// Used to look for specific parts of the state that have changed.
export type Selector<TState, TValue> = (state: TState) => TValue;

// A function used to compare if two parts of the state have actually changed.
// By default a strict equals is used when comparing however sometimes something more complex is needed.
// The TValue refers to the value returned by the Selector.
export type SelectorComparer<TValue> = (prevValue: TValue, newValue: TValue) => boolean;

// A callback function to be triggered when a selector has returned a new value.
// The callback is given the new state and result of the selector that triggered the callback.
// The TValue refers to the value returned by the Selector.
export type Subscription<TState, TValue> = (state: TState, newValue: TValue, triggeringModifier: Modifier<TState>, isNewState: boolean) => void;

// A function used to remove a subscription. This can be called multiple times.
export type RemoveSubscription = () => void;

DataStore

The main data store class. Keeps track of the current state, any subscriptions and optionally a history of the state.

By default history is disabled.

Constructor

initialState: TState The initial state of the store.

The constructor requires an initial state for the store that fits the store's state interface.

interface State
{
    readonly name: string;
    readonly age: number;
    readonly interests: string[];
}

const store = new DataStore<State>({
    name: 'Unset',
    age: 0,
    interests: []
});

state

returns: Readonly<TState> The current state.

Returns the state marked specifically as readonly. The readonly part is not usually required but just to make it very clear.

execute

modifier: Modifier<TState> The modifier function to patch the state with.

Executes a modifier on the state. The modifier is recommended to return a partial state that is merged.

If the modifier returns the same state (as compared with strict equals) or null then the state is not updated nor is any subscription triggered.

subscribe

selector: Selector<TState, TValue> A function for picking the values out of the store you want to check when changed.

subscription: Subscription<TState, TValue> A callback that will be triggered when the values returned by the selector has changed.

comparer: SelectorComparer<TValue> An optional comparer for the old and new values in the selector. Defaults to strict equals.

selectorName: string An optional name for the selector, can be used to help identify the selector when debugging.

returns: RemoveSubscription A function to remove the subscription from the store.

Subscribe a callback to be triggered when a part of the state has changed.

import DataStore from "../src";

interface State
{
    age: number;
    name: string;
}

const store = new DataStore<State>
({
    age: 30,
    name: "Fluff"
});

let numberOfAgeChanges = 0;

// Subscribe to any changes to the age by returning the age from the state which will be compared between execute calls.
store.subscribe((state) => state.age, (state, newAge) => console.log('New Age', newAge));

// Subscribe to any changes to the name by returning the name from the state which will be compared between execute calls.
store.subscribe((state) => state.name, (state, newName) => console.log('New Name', newName));

// Subscribe to any changes to the age by adding our own way of comparing age changing.
store.subscribe((state) => state, (state) => numberOfAgeChanges++, (prev, next) => next.age === prev.age);

store.execute((state) => ({age: 35}));
store.execute((state) => ({name: "Puff"}));
store.execute((state) => ({age: 40}));
store.execute((state) => ({age: 40}));

console.log('Number of age changes', numberOfAgeChanges);

/* Example output
New Age 35
New Name Puff
New Age 40
Number of age changes 2
*/

subscribeAny

subscription: Subscription<TState, TState> A callback that will be triggered when the state has changed.

selectorName: string An optional name for the selector, can be used to help identify the selector when debugging.

returns: RemoveSubscription A function to remove the subscription from the store.

A shorthand subscribe function that will trigger the callback when the state changes at all.

unsubscribeAll

Removes all subscriptions from the store.

Benchmarks

There's a benchmarks folder with more details however the summery is that SimpleDataStore is roughly similar to redux in terms of performance.

The pros are that it doesn't really matter how many state modifiers you have because there's no additional lookup time to find them compared to reducers.

The cons for very simple states, where the state is a primitive value or the reducer returns the whole state without needing to be combined it's slower than Redux.

The main reason being that SimpleDataStore assumes that the state is always an object and the modifiers will always return a partial state and combines using Object.assign. Combined to Redux that always assumes a full state object.

License

MIT

Author

Alan Lawrey 2021