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-resource-manager

v0.2.3

Published

A Redux wrapper that helps manage external resources in a client-side application.

Readme

redux-resource-manager

A Redux wrapper that helps manage external resources in a client-side application.

experimental

NPM

Example usage:

First, wrap Redux's createStore method with applyResourceManager and pass in a definition for the external resources you need (note: this is similar to the way you wrap createStore when setting up Redux middleware):

import { createStore } from 'redux';
import applyResourceManager from 'redux-resource-manager';
import rootReducer from './reducers';

const resources = {
    users: {
        buildUrl: (params) => `https://api.github.com/users/${params.username}`,
        ttl: 1000 * 60 * 5 // 5 minutes
    }
};

const wrappedCreateStore = applyResourceManager(resources)(createStore);
const store = wrappedCreateStore(rootReducer);

Second, wrap your component with connectResourceManager passing in a mapResourcesToProps function, for example:

const UserInfo = connectResourceManager(store)((props, getResource) => ({
  user: getResource.users({ username: props.username })
}))((props) => {
    const { user } = props;

    // user <-- This is a resource object. It has a few special properties:
    // user.status <-- this is the status of the request. It can be one of these three values:
    //                 ('pending' || 'fulfilled' || 'rejected')
    // user.retry <-- if a resource's status is 'rejected', this will be a function that a component
    //                can use to retry the request
    // user.result <-- if the resource's status is 'fulfilled', this will be the value returned from
    //                 the api

    if (user.status === 'pending') return <Loader />;
    if (user.status === 'rejected') return <RetryButton retry={user.retry} />;

    const { avatarURL, login, name, company, location, followers } = user.result;

    return (
        <div>
            <img src={avatarURL} />
            <h2>{login}</h2>
            <h3>{name}</h3>
            <h4>{company} - {location}</h4>
            <h5>Followers: {followers}</h5>
        </div>
    );
});

Resource definitions

The resource definition API tries to find a balance between user-friendly (with sensible defaults) and flexible (for more complicated UI/API interfaces).

const resources = {
    // Each key in the resources definition should be the name of the resource and is used when
    // fetching values from the store. For example: `store.get.users(params)`.
    users: {
        // Resource definitions require, at the least, `buildUrl` and `ttl` fields.
        // The `buildUrl` function receives the arguments passed to `store.get.users()`.
        buildUrl: (params) => `https://api.github.com/users/${params.username}`,
        ttl: 1000 * 60 * 5, // 5 minutes in milliseconds

        // Resource definitions can also include `parseResponse` and `createCacheKey` functions:
        parseResponse: (response) => {
            const { avatarURL, login, name, company, location, followers } = response;
            return { avatarURL, login, name, company, location, followers };
        },

        // `createCacheKey` receives the same arguments `buildUrl` receives. The expected return
        // value is a string to cache the response with. If no `createCacheKey` function is supplied
        // the url returned by `buildUrl` is used instead.
        createCacheKey: (params) => params.username
    }
};

Batched requests

If your API supports batched requests, you can set up your resources definition like so:

// For example, if your API accepts batched requests in the form of:
// /users?usernames=<comma-separated list of usernames>
const resources = {
    users: {
        // You must supply a `buildBatches` function which takes all the requested resources within
        // a 50ms window and should return an array of batches. In this example, we just cut up the
        // requests into batches of 10. Note: you'll probably want to dedupe the list as multiple
        // components might have requested the same resource, depending on the complexity of your app.
        buildBatches: (requestedResources) => {
            const resources = dedupe(requestedResources, params => params.username);
            return resources.map(() => (resources.splice(0, 10)));
        },

        // Your `buildUrl` function will receive a batch of params, and it must return a url.
        buildUrl: (batch) => {
            const usernames = batch.map(params => params.username);
            const usernamesQueryParam = encodeURIComponent(usernames.join(','));
            return `https://api.github.com/users?usernames=${usernamesQueryParam}`;
        },


        // For batched requests, the `unbatchResponse` function is required. It is used to map the
        // requested resources' params to the relevant part of the response.
        // NOTE: This API is lacking in some ways and will most likely change in the future.
        // Use at your own risk!
        unbatchResponse: (batchedParams, batchedResponse) => {
            const users = {};
            batchedResponse.forEach(u => users[u.login] = u);
            const fulfilled = [];
            const rejected = [];

            batchedParams.forEach(params => {
              const user = users[params.username];
              if (!user || typeof user === 'string') {
                rejected.push({
                  params,
                  error: user || `${params.username} not found in response`
                });
              } else {
                fulfilled.push({
                  params,
                  result: user
                });
              }
            });
            return { fulfilled, rejected };
        },

        // For batched requests, `createCacheKey` is also required. It receives a requested params object
        // and should return a string to cache the result with.
        createCacheKey: (params) => params.username,

        ttl: 1000 * 60 * 5, // 5 minutes in milliseconds
    }
};

Goals

  • Colocate data requirements with a component
  • Provide data caching
  • Declaratively define external resources
  • Provide easy way to manage showing Loader and Error components (and retry ajax requests)

FIXMEs

  • the entire store gets passed to components
  • the store must be explicitly passed to connectResourceManager
  • Components have to know about the cache structure (e.g. users.result)
  • Provide sensible default for batching/deduping? For example:
{
  ...,
  batchSize: 10,
  dedupeWithField: (params) => params.username,
}

Scripts

To develop against the demo:

npm run batching-server
npm run develop:demo

Also, important:

  • npm run test - runs mocha tests
  • npm run lint - runs eslint

For a list of all the scripts, run npm run from this project's root.

License

MIT, see LICENSE.md for details.