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

redux-store-watch

v0.0.13

Published

A watcher for redux store.

Downloads

49

Readme

Watch paths on redux's store.

The objective of this utility is to watch specific paths on redux's store. This is achieved by either providing a selector function whose results will be compared between current and previous state, or a string path whose value will be checked on the current and previous state. All differences are checked with strict equality. If there is a need to do an equality check with anything other than strict equality, watch accepts an optional config checkEqual, which accepts current and previous values.

$ npm install --save redux-store-watch

Include the package.
import watchStore from 'redux-store-watch';

or

const watchStore = require('redux-store-watch');
Import your redux store. Assume imported as store.

Using Redux component as example:

Here is how we would watch some string path's on the store.
class MyComponent extends Component {
  componentWillMount() {
    this.watcher = watchStore(store);

    this.watcher('user.email', (currentValue, previousValue, currentState, previousState) => {
      // store's `user.email` has changed

      // current is current value of user.email
      // previous is previous value of user.email
      // state is the full current state.
    });

    this.watcher('user.name', (currentValue, previousValue, currentState, previousState) => {
      // store's `user.name` has changed
    });

    // We don't have to watch specific values, we can just watch for any changes
    // on store's `user` path. Note that this watcher would also be called if either
    // of the previous watchers were called, as if `user.email` or `user.name` has
    // changed on an immutable store, `user` has necessarily changed.
    this.watcher('user', (currentValue, previousValue, currentState, previousState) => {
      // store's `user` has changed
    });

    // We can also use a custom equality checker.
    this.watcher('user', () => {
      // store's user has changed
    }, {
      checkEqual: (currentValue, previousValue) => _.isEqual(currentValue, previousValue)
      // or for short: checkEqual: _.isEqual
    });
  }

  componentWillUnmount() {
    // We unsubscribe from the store. If you do not do this, in the same way as
    // if you had no unsubscribed from redux, the callbacks would continue to
    // be called even if the component were no longer mounted. 
    this.watcher.remove();
  }
}
Here is how we would watch the results of a selector.
class MyComponent extends Component {
  componentWillMount() {
    this.watcher = watchStore(store);

    // Let's say this is my selector, could be defined in another file. The
    // selector will always be provided the current state as an argument.
    const userEmailSelector = state => state.user.email;

    this.watcher(userEmailSelector, (currentValue, previousValue, currentState, previousState) => {
      // results of userEmailSelector has changed

      // current is current value of the selector's result
      // previous is previous value of selector's result
      // state is the full current state.
    });


    // If you have a selector that requires specific arguments be passed in,
    // you can simply wrap the selector in a closure.

    // Say that this was my selector.
    const userValueSelector = (prop, state) =>  state.user[prop];

    // Because my selector is expecting something different than just the `state`
    // argument passed in by the watcher, I can just wrap it in a function.
    this.watcher(state => userValueSelector('email', state), (currentValue, previousValue, currentState, previousState) => {
      // results of userValueSelector('email')
    });


    // As a last example, each selector's result is actually stored since the last
    // change, so you could really put whatever you want as a function whose
    // result you'd like to compare to last time a redux action was dispatched.
    this.watcher(state => Math.random(), (currentValue, previousValue, currentState, previousState) => {
      // A random number was different than the last time an action was dispatched...
      // Absolutely fascinating.
    });
  }

  componentWillUnmount() {
    this.watcher.remove();
  }
}

Logging

Reactive programming of this nature has a lot of benefits, but has the downside of often being hard to debug. If I add a watcher on a value change with no logging, side effects are occuring that would be very hard to track down.

One of the reasons redux is so easy to debug is because of the redux logger letting you know which actions were dispatched. Without it, actions could be reduced all over the place without an easy way to track down which actions even dispatched.

In order to mitigate this, you have the option to pass a name to the watcher, like the name of an action. If shouldDispatch or shouldLog are enabled, details including the name, selector, path (if applicable), previous & current values will be logged or dispatched as an easy way to track down which watchers have been triggered.

Naming Convention

A good convention I've found for naming your watcher is:

{ caller description } << { watched target description }

For a concrete example, imagine a structure where I had an auth reducer somewhere in my application, responsible for holding the active user token. In my Login View, let's say that I'd like to watch the token being set.

watcher.watch('shared.auth.token', (token) => {
  // Do something about this token.
}, {
  name: 'LOGIN_VIEW << AUTH.TOKEN'
});

So to enable logging/dispatching for an individual watcher:

const watcher = watchStore(store, {
  shouldDispatch: true,
  shouldLog: true
});

To be more fine grained, the same logging/dispatch config is available on a per-listener basis.

watcher.watch('shared.user.email', () => {
  // Thing changed!
}, {
  name: 'LOGIN_VIEW << USER.EMAIL',
  shouldDispatch: true,
  shouldLog: true
})

It is recommended that you use requireName: true, and provide names, just think how much easier redux is because of its logging!

Global Config

Global Config allows us to configure logging, dispatching on a global basis, shared by all watchers in your application.

You are also able to set the store here, if you have a single store and would rather just set it once during your bootstrapping process.

import watchStore from 'redux-store-watch';
watchStore.configureGlobal({
  store,
  shouldDispatch: true,
  shouldLog: true,
  requireName: true
});

Now when creating a new watcher I can simply do:

import watchStore from 'redux-store-watch';

const watcher = watchStore();

Without providing a store, the watcher will fall back on global.