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

@warren-bank/redux-filter-subscriptions-enhancer

v3.0.0

Published

redux store enhancer: `store.subscribe(listener, filter)` -> `listener(newState, action)`

Downloads

4

Readme

redux-filter-subscriptions-enhancer

Overview:

  • This project contains a Redux "Store Enhancer"
  • Its purpose is two-fold:
    • store.subscribe(listener, filter)
      • adds a new (optional) input parameter: filter
        • its purpose is to conditionally filter when listener is called
        • when its value is:
          • function:
            • input: (oldState, newState)
            • output: boolean
              • when truthy, listener is not called for this state change
          • string:
            • treated as a jsonpath pathExpression
              • precondition: state must be an Object
                • will throw an Error when the root reducer returns a state that's not an Object
              • pathExpression uniquely defines the path to one specific data structure within the state Object
            • filter looks up the value of this data structure in both oldState and newState
              • these values are compared for equality (by reference)
                • if equal, listener is not called for this state change
          • boolean:
            • if true:
              • oldState and newState are compared for equality (by reference)
                • if equal, listener is not called for this state change
                  • note: this is optimized but functionally equivalent to the jsonpath pathExpression: "$"
          • undefined | false | any other value:
            • default behavior
              • listener is always called after the root reducer function completes processing a dispatched action
    • listener(newState, action)
      • passes information to the listener
        • newState is the same value returned by store.getState()
        • action is the value received by store.dispatch() that caused the reducer functions to change state
          • it's debatable whether there's any reason a listener should ever use this value
          • it's officially considered by the Redux team to be an anti-pattern
          • but..
            • one-size-fits-all rules don't work for every situation
            • there may be a valid reason to want this value
            • if a project is using middleware to debounce actions
              • it is the author's responsibility to be aware of this fact
          • imho..
            • better to have the data available without any reason to refer to it, than to have the data unavailable and being in the rare situation that it is needed

Background:

  • I submitted a pull-request to the Redux repo with a minimal patch to add the enhancement:
    • store.subscribe(listener, filter)
  • It got turned down flat:
    • "We have no plans to implement this. If you'd like to do it yourself, you can implement it as a store enhancer."
  • So.. I did, and here we are

Installation:

npm install --save '@warren-bank/redux-filter-subscriptions-enhancer'

Usage Example #1:

  • This calling pattern allows the middleware enhancer to modify the base redux store before our enhancer
  • The result is that calls to store.dispatch(action) pass through:
    • our enhancer
      • all middleware
        • base redux store
  • The effect is that the action passed to listener has not been modified by middleware
    • in most cases, this would be undesirable
    • listener will most-likely want to receive the action exactly as it is received by the base redux store
const { applyMiddleware, createStore } = require('redux')
const enhancer     = require('@warren-bank/redux-filter-subscriptions-enhancer')
const rootReducer  = (state, action) => (action.type === "test") ? state + 1 : state
const initialState = 0
const middleware   = applyMiddleware(logger, thunk, etc)
const store        = enhancer(createStore)(rootReducer, initialState, middleware)

const listener     = (newState, action) => console.log(JSON.stringify({newState, action}, null, 2))
const filter       = (oldState, newState) => newState % 2 !== 0

store.subscribe(listener, filter)

rundown:

  • the state is an integer that is initialized to 0, and its value increments by 1 every time a "test" action is dispatched to the store
  • when listener is called, it logs its input parameters to the console as a serialized Object containing descriptive keys
  • when listener subscribes to the store, it includes a filter function that will apply custom conditional logic to determine when listener should be called
    • in this case, listener wants to be informed each time the state changes to an even value

reminder:

  • unlike the filter function for an Array, which returns true to include values..
  • this function will block calls to listener when the filter function returns true

to run tests containing code similar to usage example #1:

git clone "https://github.com/warren-bank/redux-filter-subscriptions-enhancer.git"
cd "redux-filter-subscriptions-enhancer"

cd "browser-build"
npm install

cd "../tests"
npm install
npm run test

Usage Example #2:

  • This calling pattern is effectively identical to usage example #1
    • but it opens the door..
const { applyMiddleware, compose, createStore } = require('redux')
const enhancer     = require('@warren-bank/redux-filter-subscriptions-enhancer')
const rootReducer  = (state, action) => (action.type === "test") ? state + 1 : state
const initialState = 0
const middleware   = applyMiddleware(logger, thunk, etc)
const rootEnhancer = compose(enhancer, middleware)
const store        = createStore(rootReducer, initialState, rootEnhancer)

const listener     = (newState, action) => console.log(JSON.stringify({newState, action}, null, 2))
const filter       = (oldState, newState) => newState % 2 !== 0

store.subscribe(listener, filter)

Usage Example #3:

  • This calling pattern reverses the order, and allows our enhancer to receive dispatched actions after they have been processed by middleware
const { applyMiddleware, compose, createStore } = require('redux')
const enhancer     = require('@warren-bank/redux-filter-subscriptions-enhancer')
const rootReducer  = (state, action) => (action.type === "test") ? state + 1 : state
const initialState = 0
const middleware   = applyMiddleware(logger, thunk, etc)
const rootEnhancer = compose(middleware, enhancer)
const store        = createStore(rootReducer, initialState, rootEnhancer)

const listener     = (newState, action) => console.log(JSON.stringify({newState, action}, null, 2))
const filter       = (oldState, newState) => newState % 2 !== 0

store.subscribe(listener, filter)

to run tests containing code similar to usage examples #2 and #3:

git clone "https://github.com/warren-bank/redux-filter-subscriptions-enhancer.git"
cd "redux-filter-subscriptions-enhancer"

cd "browser-build"
npm install

cd "../tests"
npm install
npm run test

Browser Build (transpiled to ES5):

  • files in repo:

  • files hosted in CDN:

  • global variable(s):

    • window.redux_filter_subscriptions_enhancer
  • conditional dependencies:

    • jsonpath is not bundled into the browser build
      • it was briefly in v2.0.0
        • the size increased from 1.06 KB (v1.0.0) to 164 KB (v2.0.0)
      • support for jsonpath filters is now an optional feature
      • to enable this feature:
        • include the additional browser build script: jsonpath
      • to use it:
        • call store.subscribe(listener, filter)
          • assign to filter a string value containing a well-formatted pathExpression
        • example:

Legal: