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-resx

v1.0.7

Published

(Yet another) Redux action creators, a reducer and middleware for resource-based APIs

Downloads

26

Readme

Redux API Resource creator (redux-resx)

Build Status

Yet another Redux action creators, a reducer and middleware for resource-based APIs.

resx = shortening for resource

Based on the async actions pattern in redux (https://redux.js.org/docs/advanced/AsyncActions.html)

Installation

npm install --save redux-resx

Usage

Resource Definition

A resource is a grouping of redux reducers, actions and selectors for your api endpoint. You define a unique name for it and the url. You can also add your own reducer to augment the state at the "mountpoint" on the state store.

// somewhere like src/resources/user.js
import createResource from 'redux-resx';

export default createResource({
  // Required:
  name: '@resx/USER', // Unique namespace for actions and reducer
  url: '/users',

  // Optional (defaults shown)
  // This function should return the root object of where you mount your state
  baseSelector: s => s.resources,
  // Use this to add extra reducers which receive the state after the built-in reducer
  // has done it's thing - this can perhaps be used in conjunction with custom middleware
  // It only receives resource actions, not every action
  reducer: (state, _action) => state,
});


// src/resources/index.js
export default as user from './user';

Reducer

import { combineReducers } from 'redux';

import * as resources from '../resources';
import { reducer as resourceReducer } from 'redux-resx';

export default combineReducers({
  resources: combineReducers(resourceReducer(resources)),
});

Lets break this down a bit:

export default combineReducers({
  // 'resources' can be anywhere, you just need to specify a base selector that selects it in
  // create resource
  resources: combineReducers(
    // resourceReducer is really just transforms the object to something that combineReducers can use
    // Give it { users: [result of createResource] } and it will return { users: reducerFn } - simple
    resourceReducer(resources)
  ),
});


// Another way you could do this

import userResource from '../resources/users';
...
resources: combineReducers({
  myUser: userResource.reducer,
  //etc
})

Component

Please see the NB comments

import React, { PropTypes } from 'react';
import { connect } from 'react-redux';

import { user as userResx } from '../resources';

// NB: New in 1.0.0+
// *************************************************************************
// You need to provide a namespace for your 'instance' (any string) that you want to use.
// This is so you can call a resource in multiple components without interferance.
const myUserResx = userResx.create('@HOME');
// If you omit the namespace, a default one will be used (essentially the same behaviour prior to 1.0.0)
// const myUserResx = userResx.create();
// Using the default method, returns a singleton instance for reuse
// const myUserResx = userResx.default();

const Home = React.createClass({
//....

  componentWillMount() {
    const { getUser, findUsers, resetUsers } = this.props;
    // XXX: Optional, you'll get old results before new ones are loaded if you don't do this.
    // New in 1.0.0: If your resource is only used in this component and you destroy on unmount,
    // you definitely/obviously won't need to use reset.
    resetUsers();
    findUsers({ status: 'active' }); // params of request
    getUser(123).then(...); // id=123 NB: only if middleware returns a promise
  },

  // NB: New in 1.0.0 - will remove the namespaced data entirely
  componentWillUnmount() {
    this.props.destroyResx();
  }

  render() {
    const { users, user } = this.props;

    return (
      <div>
        {users ? JSON.stringify(users) : null}
        {user ? JSON.stringify(user) : null}
      </div>
    );
  },
});

function mapStateToProps(state) {
  // Select the resource state
  const {
    hasLoaded, // true when find has been loaded before
    isBusy, // true when any of the following are true
    isFinding,
    isGetting,
    isCreating,
    isUpdating,
    isPatching,
    isRemoving,

    // Result for find - always an array (initial value: [])
    items,

    // Last result for create, get, update, patch, remove
    entity, // (initial value: undefined)
  } = userResx.selector(state);

  return {
    users: items,
    user: entity,
    isBusy,
  };
}

const { find: findUsers, get: getUser, reset: resetUsers, destroy: destroyResx } = myUserResx.actions;

export default connect(mapStateToProps, {
  findUsers,
  getUser,
  resetUsers,
  destroyResx,
})(Home);

Selector

Each resx has a selector function which can be used to select the resource from the state store.

A resx has the following structure:

// Initial structure of resx
{
  hasLoaded: false, // Has the resource loaded before (has find returned a result and items populated)
  isBusy: false, // true if any operation is running on this resx, otherwise false
  isFinding: false, // true if find call is busy, otherwise false
  isGetting: false, // true if get call is busy, otherwise false
  isCreating: false, // true if create call is busy, otherwise false
  isUpdating: false, // true if update call is busy, otherwise false
  isPatching: false, // true if patch call is busy, otherwise false
  isRemoving: false, // true if remove call is busy, otherwise false
  items: [], // The result of a find call
  entity: undefined, // The result of the last get, create, patch, update and remove call
  lastError: undefined, // The result of the call if it was an error
}

Middlewares

The middleware's job is to "handle" the actions coming in from resource action creators. This is where the side-effects are. A middleware is included which calls endpoints like you would expect, but you can implement your own or use e.g. sagas.

Builtin middleware

Add middleware to store in the normal way

// NB: Only bundled if you are using it
import middleware from 'redux-resx/middleware';
import fetch from 'isomorphic-fetch';
//... other imports

const resxMiddleware = middleware({
  baseUrl: '/api',
  provider: fetch, // could write adapter to convert fetch params to e.g. request/jquery
});

export default function createApplicationStore() {
  return createStore(
    reducers,
    compose(applyMiddleware(resxMiddleware))
  );
}

Other middleware

TODO

  • Example

Future Ideas

  • Middleware (separate package) that implements a decoupled cache using state