Socket
Book a DemoInstallSign in
Socket

redux-resx

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

redux-resx

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

1.0.7
latest
Source
npmnpm
Version published
Maintainers
1
Created
Source

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

  • redux-resx-feathers-middleware - uses feathers-client to make requests
  • redux-resx-saga - sagas to handle resource actions (TODO)

TODO

  • Example

Future Ideas

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

FAQs

Package last updated on 16 May 2018

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

About

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.

  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc

U.S. Patent No. 12,346,443 & 12,314,394. Other pending.