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

@intactile/redux-normalized-module

v0.3.1

Published

A library for generating a redux normalized module

Downloads

8

Readme

redux-normalized-module

A library for generating a redux normalized module

Build Status Maintainability Test Coverage

The genereated module provides a reducer, the action creators and the selectors to manage a normalized state.

Installation

npm install @intactile/redux-normalized-module

or

yarn add @intactile/redux-normalized-module

Normalized state

A normalized state is structured like this:

{
    byId: {
        1: { id: 1, name: 'France', continentId: 1 },
        2: { id: 2, name: 'Croatia', continentId: 1 },
        3: { id: 3, name: 'Belgium', continentId: 1 },
        4: { id: 4, name: 'England', continentId: 1 },
        5: { id: 5, name: 'Brasil', continentId: 2 },
    },
    allIds: [1, 2, 3, 4]
}

Indexes can also be added.

{
    ...,
    byContinentId: {
        1: [1, 2, 3, 4],
        2: [5]
    }
}

Configuration

A normalized module is created from a definition.

const moduleDefinition = {
  selector: state => state.countries,
  reducers: {
    // map an action type with a generic reducer
    CREATE_COUNTRY: "create",
    UPDATE_COUNTRY: "update",
    REPLACE_COUNTRY: "replace",
    DELETE_COUNTRY: "delete",
    LOAD_COUNTRIES: {
      type: "load",
      extractor: action => action.payload.countries
    }
  }
};

const module = createReduxNormalizedModule(moduleDefinition);

The generated module will expose a reducer:

import { createStore, combineReducers } from "redux";

store = createStore(combineReducers({ countries: module.reducer }));

some action creators:

store.dispatch(module.actions.create({ name: "Argentina", continentId: 2 }));
store.dispatch(module.actions.update(6, { name: "Chile" }));
store.dispatch(
  module.actions.replace({ id: 6, name: "Chile", continentId: 2 })
);
store.dispatch(module.actions.delete(6));
store.dispatch(module.actions.toFront(2));

and some selectors:

const state = store.getState();
module.selectors.getAllIds(state);
module.selectors.getAll(state);
module.selectors.getById(state, 6);
module.selectors.isEmpty(state);
module.selectors.getLastCreated(state);
module.selectors.getLastCreatedId(state);
module.selectors.getNextId(state);

Add a comparator

The state could be sorted with a comparator:

const sortByName = (o1, o2) => o1.name.localeCompare(o2.name);
const moduleDefinition = {
  comparator: sortByName
};

By doing this, the allIds array with automatically sorted when an object is created or updated.

module.selectors.getAllIds(state); // => the ids sorted by `name`

Note: the toFront action can't be used when a comparator is configured

Add a many to one index

For performance purpose, one or more indexes might be added to the state. They are updated when an object is created, updated or deleted.

const moduleDefinition = {
  indexes: [{ attribute: "continentId" }]
};

The generated module provides an index selector:

const state = store.getState();
selectors.byContinentId.get(store.getState(), 1); // => [1, 2, 3, 4]
selectors.byContinentId.get(store.getState(), 3); // => []

The index can also be sorted with a comparator:

const sortByName = (o1, o2) => o1.name.localeCompare(o2.name);
const moduleDefinition = {
  indexes: [{ attribute: "continentId", comparator: sortByName }]
};

The attribute property can also be an object to handle more complex use case it should provide a name property, this will be use to generate the name of the index and it should also provide a custom function to compute the keys of the index, this function will be called with the object as parameter:

const moduleDefinition = {
  indexes: [{
    attribute: {
      name: "isColored",
      computeKey: (object) => !!object.color
    }
  }]
};

Add a one to one index

const moduleDefinition = {
  indexes: [{ attribute: "name", oneToOne: true }]
};

The index selectors are:

const state = store.getState();
selectors.byName.get(store.getState(), "France"); // => { id: 1, name: 'France', continentId: 1 }
selectors.byName.exists(store.getState(), "Germany"); // => false

Complete module definition

const moduleDefinition = {
  indexes: [
    { attribute: "continentId", comparator: sortByName },
    { attribute: "name", oneToOne: true }
  ],
  selector: state => state.countries,
  reducers: {
    CREATE_COUNTRY: "create",
    UPDATE_COUNTRY: "update",
    REPLACE_COUNTRY: "replace",
    DELETE_COUNTRY: "delete",
    LOAD_COUNTRIES: {
      type: "load",
      extractor: action => action.payload.countries
    }
  }
};