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

entity-store

v2.1.0

Published

Dynamic actions and reducers for collection-based application states

Downloads

673

Readme

entity-store

Dynamic actions and reducers for collection-based application states

entity-store is a modest library for defining and manipulating application states that are composed of entity collections. It comes without dependencies, is field-tested, and not biased towards a particular framework.

import { CompositeEntityAction, createEntityState, createEntityReducer } from "entity-store";

// 1) Define an entity with an initial state and a dedicated reducer
const userType = "User";      
const initialUsersState = createEntityState();  
const usersReducer = createEntityReducer({
    entityType: userType, initialUsersState
}); 

 // 2) Update the state with dynamic actions
const action = new CompositeEntityAction({                             
    set: [{
        entityType: userType,
        payload: {
            "user01": {
                label: "Jason"
            }
        }
    }]
});
const updatedState = usersReducer(initialUsersState, action);

Getting started

Install the package via npm

npm install --save entity-store

Store some entities! The following Quick reference and the included TypeScript definitions show you how.

Quick reference

Defining entity states with interfaces

Note: You can skip this section if you're not using TypeScript.

Use the EntityState interface to declare states for each entity type you would like to manage in your store.

import { EntityState } from "entity-store";

export interface User {
    label: string;
}

export interface AppState {
    users: EntityState<User>;
}

Modifying states with CompositeEntityAction

Entity states are modified with a defined set of operations.

Basic

  • Add: Adds entities to a collection
  • Update: Updates specified entities in a collection
  • Remove: Removes specified entities from a collection

Convenience

  • Clear: Removes all entities from a collection
  • Set: Replaces all entries in a collection with a new set
  • Select: Mark specified entities in a collection as selected

CompositeEntityActions are composed of these operations.

The following example shows how to construct such an action. It is an instruction to add an entity of type "User" with the label "Jason" and id "user01" to our state and to update the population of the "Location" with id "location01" with the value 20001.

import { CompositeEntityAction } from "entity-store";

const action = new CompositeEntityAction({
    add: [{
        entityType: "User",
        payload: { "user01": { id: "user01", label: "Jason" } }
    }],
    update: [{
         entityType: "Location",
         payload: { "location01": { population: 20001 } }
    }]
});

Types for those actions are created dynamically for each combination of operation, entity, and (optional) store feature. For the above action the result would be [Composite] [User] Add [Location] Update'.

Processing entity actions with matching reducers

Entity reducers execute the operations described by those actions.

They are really easy to set up. Just provide an entity type to createEntityReducer.

import { createEntityReducer } from "entity-store";

const userReducer = createEntityReducer({
    entityType: "User"
});

Utilities

entity-store comes with some utility function that make passing data to CompositeEntityActions and selecting data from state easier.

createKVSFromArray

Add, Update, and Set operations in a CompositeEntityAction want a key-value store as payload, so it is often necessary to map data from an array into this form.

import { createKVSFromArray } from "entity-store";

const users = [{
    id: "user01",
    label: "Jason"
}, {
    id: "user02",
    label: "Carsten"
}];

const userKVS = createKVSFromArray(users);

By default, createKVSFromArray assumes that the passed items have an attribute called 'id'. You can pass a different attribute name if your id is stored in a different member.

Note: TypeScript users might want to use keyof here.

import { createKVSFromArray, User } from "entity-store";

const users = [{
    userId: "user01",
    label: "Jason"
}, {
    userId: "user02",
    label: "Carsten"
}];

const userKVS = createKVSFromArray(users, "userId" as keyof User);

If the id is not expressed in a single attribute value, you can also pass a function that receives an item and returns a string as second argument to createKVSFromArray.

import { createKVSFromArray, User } from "entity-store";

const users = [{
    userCollectionId: "myCollection",
    userNumber: 1,
    label: "Jason"
}, {
    userCollectionId: "myCollection",
    userNumber: 2,
    label: "Carsten"
}];

const userKVS = createKVSFromArray(
    users, (x: User) => x.userCollectionId + x.userNumber
);

getAll

Often, you want to work with your entities as arrays. When writing logic to select items from an entity state, you can use getAll to achieve this.

import { getAll } from "entity-store";

const userState = {
    ids: ["user01", "user02"],
    entites: {
        "user01": {
            id: "user01",
            label: "Jason"
        },
        "user02": {
            id: "user02",
            label: "Carsten"
        }
    }
};

const allUsers = getAll(userState);

getFirstSelected

A frequent use case is to mark an entity as selected. Entity store does this by putting its id in selectedIds. getFirstSelected returns the entity associated with the first entry in this collection and is useful for modelling single selection.

import { getFirstSelected } from "entity-store";

const userState = {
    ids: ["user01", "user02"],
    entites: {
        "user01": {
            id: "user01",
            label: "Jason"
        },
        "user02": {
            id: "user02",
            label: "Carsten"
        }
    },
    selectedIds: ["user01"]
};

const selectedUser = getFirstSelected(userState);

Advanced features

Working with store features

It can be convenient to isolate regions in your store that are independent from your main state. Those special store features can be used by setting the storeFeature option when using createEntityReducer and CompositeEntityAction.

import { createEntityReducer, CompositeEntityAction } from "entity-store";

const userReducer = createEntityReducer({
    entityType: "User",
    storeFeature: "UserManagement"
});

const action = new CompositeEntityAction({
    add: [{
        entityType: "User",
        payload: { "user01": { id: "user01", label: "Jason" } },
        storeFeature: "UserManagement"
    }]
});

Configuring action-type composition

If you are not happy with how the dynamic action types are created, you can create your own CompositeEntityActionConfig and pass it to createEntityReducer and CompositeEntityAction.

import { 
    createEntityReducer, CompositeEntityAction, CompositeEntityActionConfig 
} from "entity-store";

const customCompositeEntityActionConfig: CompositeEntityActionConfig = {
    prefixes: {
        composite: "[Composite]",

        add: "Add",
        remove: "Remove",
        select: "Select",
        update: "Update",
        set: "Set",
        clear: "Clear",
    },
    separator: " | ",
    spacer: " "
};

const userReducer = createEntityReducer({
    entityType: "User"
}, {
    compositeEntityActionConfig: customCompositeEntityActionConfig
});

const action = new CompositeEntityAction({
    add: [{
        entityType: "User",
        payload: { "user01": { id: "user01", label: "Jason" } }
    }]
}, customCompositeEntityActionConfig);