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 🙏

© 2026 – Pkg Stats / Ryan Hefner

redux-list-reducer

v0.0.4

Published

Reducer factory that implements common list actions

Readme

redux-list-reducer

redux-list-reducer is a factory function for creating Redux reducers that operate on lists.

This is an early version and still a work in progress, do not use in production yet, expect the interface to change.

Read Thoughts before using this module.

Current Approach

The reducer works on lists, which are expected to contain unique items among all lists. The items can be of any type as long as for any two items a and b a !== b holds. At the moment there are no checks that the user doesn't include the same item twice.

There are two practical options for items:

  • items are JavaScript objects
  • the objects themselves are kept in a separate recuder and the items are object IDs (integer or UUID) (normalized schema)

The user is expected to handle the data in immutable fashion. Currently either JavaScript collections (default) or ImmutableJs can be used. The idea is to develop a well-tested and performant implementation of basic actions with lists.

Exported action creators:

| Action creator | Explanation | | --------------- | ------- | | push(item, list) | Add item to list | | del(item) | Delete item if it exists on any list, otherwise return the current state | | update(item, newItem) | Update item with newItem | | move(item, toItem, before = true) | Move item before/after toItem (Currently if toItem is not found item just gets deleted) | | moveToList(item, list) | Moves item to list, use this if the list is empty | | toggleProperty(property, item) | See Properties |

Usage

See the example app under example/ and tests. Particularly, teams.js shows how the library is used.

In your reducer implementation that operates on lists of data:

import listreducer from 'redux-list-reducer'

// Define your own reducer
// Rather than exporting this directly to use in `combineReducers`, pass this
// as an argument to `listreducer`. Redux never calls this function directly,
// but listReducer first tries to match its own actions, then if none
// match it calls this function with the current state and action.

const wrappedReducer = (state, action) => {
  switch (action.type) {
    case LOAD:
      return action.result
    default:
      return state
  }
}

// Create the final reducer and get the default actions

const {reducer, actionCreators} = listreducer({
  initialState: [],
  itemsProperty: 'items',
  wrappedReducer
})

export default reducer

// Define your actions (they can use actions exported by listreducer or
// be completely independed), export them and the listreducer actions
// you want to expose to the user

The state listreducer operates on is an array list list objects. This can either be passed as initialState or loaded by the actions in wrappedReducer. The following could have been used as initialState in the example above:

const initialState = [
  {
    items: [
      {name: 'foo'},
      {name: 'bar'},
    ]
  },
  {
    items: [
      {name: 'baz'},
    ]
  }
]

The property items was defined as itemsProperty in when calling listreducer above.

To use ImmutableJS, you need pass format: 'immutable' to listreducer, and the state needs to be an Immutable.List and the list objects Immutable.Map. To use the initialState from above:

import Immutable from 'immutable'

const {reducer, actionCreators} = listreducer({
  initialState: Immutable.fromJS(initialState),
  format: 'immutable'
  itemsProperty: 'items',
  wrappedReducer // needs to operate on Immutable objects
})

Properties

Properties can be used to track if an item is e.g. selected or being edited. Used properties must be passed to listreducer as params:

const {reducer, actionCreators} = listreducer({
  itemsProperty: 'items',
  properties: ['selected', 'editing']
})

Checking if property is active for item: list[property].has(item). For example, if we're using React and rendering a list of players in a team, showing either <EditPlayer> or <Player> component based on property editing:

<ul>
  {
    team.players.map((player, index) =>
      team.editing.has(player) ?
          <EditPlayer ... />
        : <Player ... />
    )
  }
</ul>

Then the editing state of the player can be toggled with this.props.toggleProperty('selected', player) assuming the toggleProperty action creator is bound to the component.

Installation

Install from npm as a dependency of your project:

npm install redux-list-reducer --save

If you want to hack on the project, clone the repo, build it, then use npm link to access it from your project:

git clone https://github.com/mattikl/redux-list-reducer
cd redux-list-reducer
npm install
npm build
npm link
cd /path/to/your/project
npm link redux-list-reducer

Thoughts

I started implementing drag & drop within a list and between lists, then realized that with current tools all complexity lies in the reducer implementation. This module is my attempt to abstract away that complexity and make reducer implementation simple. For simple data models it succeeds in this, as can be seen in the example app.

For larger applications the correct approach seems to be normalizing nested API objects, then placing each object in its own context reducer, and the list reducers contain IDs to these objects. flux-react-router-example is an example of this using normalizr. This can be accomplished in placing list items in a separate reducer and storing their IDs in the list reducer.

I have yet to see any reducer factory gain widespread usage. It looks like the primary design goal is to keep reducers so simple that no factories are needed. redux-crud is an example of a library that provides standard actions and reducers for Redux CRUD Applications, and you can also wrap its recuder in your own reducer.

When using a factory like this, you need to understand how it works. The question is how much of its internals you need to understand, and can you count on the internals to stay the same (semantic versioning provides guarantees that the interface won't change in a backwards incompatible manner but says nothing about the internals). The factory approach helps you to start out quick, so it may be a good tool for prototyping.

On the one hand using object identity instead of external IDs feels more correct, ot the other using IDs can make things much simpler.

I will continue developing this project and learning more, but can say nothing at the moment where this project is going. Comments and ideas welcome.

Thanks