redux-normalized-crud

2.1.1-beta7 • Public • Published

redux-normalized-crud

Updated DOCS coming soon

npm version

An attempt to standardize RESTful calls and responses within a growing redux application.

Installation

To install the latest version:

npm install --save redux-normalized-crud

This assumes you are using npm as your package manager.

This library uses:

In it's current version, you're expected to use these libraries in your application:

What do you get out of using it?

This library provides you with a suite of standardized sagas, reducers, action creators, and selectors to make all CRUD calls super easy.

  • Standardized server responses turned into entities
  • Standardized paging with grouping
  • Optimistic update, create, and delete server requests out of the box

Example Usage:

Create a config file that exports an object

  // config.js
  import { normalize } from 'normalizr';
  export default {
    baseUrl: 'http://localhost:3000/api/',
    // This is a callback you hook into when the server comes back with an OK (Status 200) response
    // You normalize it depending on how your sever returns paged lists, or resources
    normalizeResponse: (response, schema) => {
      let normalizedResult;
      if (Array.isArray(response.data) && 'data' in response) {
        // Paged Result set
        normalizedResult = normalize(response.data, [schema]);
      } else if(Array.isArray(response)) {
        // Just an array of items
        normalizedResult = normalize(response, [schema]);
      } else {
        // Just an object
        normalizedResult = normalize(response, schema);
      }
      return normalizedResult;
    },
    /*
       Here you define where the total items key is on the response object
       In this case the response looks like:
       
       {
        total: 5,
        data: [
          {},
          ... more data
        ]
       }
    */
    paginationResponseKeys: {
      totalItems: 'total'
    },
    // This is the callback gets triggered when the server response is not Ok 
    onServerError: (e) => {
      // This is a fetch response
      const statusCode = e.response.statusCode;
      // do something
    }
  };

Note: This can be defined on for every resource in your system or you could define 1 per resource if you wanted

Register your entity

  // post.js
  import { schema } from 'normalizr';
  import { registerEntity } from 'redux-normalized-crud';
  
  // Grab the config file we just made
  import config  from './config';
  
  // Use normalizr here to create your entity
  const postEntity = new schema.Entity('POSTS');
  
  export const {
    sagas, // All crud actions for a given entity (this gets registered with your redux-saga) 
    constants, // An object full of post constants that contain all of the crud actions
    creators, // An object full of all the action creators for the post entity
    entitySelector, // A helpful selector that is ready to key off the post entities
    groupSelector, // Another helpful selector ready to key off of your defined paged lists
  } = registerEntity(config, postEntity);

Register the sagas and enhance your reducer

  // store.js
  import { createStore, applyMiddleware, compose } from 'redux';
  import createSagaMiddleware from 'redux-saga';
  import { combineWithCrudReducers } from 'redux-normalized-crud';
  import { sagas as postCrudSagas } from './post';
  
  // SAGAS
  const mySaga = function* () {
    yield [
      postCrudSagas(),
      // any other crud sagas go here
    ];
  };
  
  const sagaMiddleware = createSagaMiddleware();
  
  /*
    Wrap your reducer with "combineWithCrudReducers". This acts the same as
    combine with reducers. You can put other reducers in that empty object.
  */
  const rootReducer = combineWithCrudReducers({});
  
  const initialState = {};
  
  const enhancer = applyMiddleware(sagaMiddleware);
  
  const store = createStore(
    rootReducer,
    initialState,
    enhancer
  );
  
  sagaMiddleware.run(mySaga);
  
  export default store;

Fire off actions in your components

// Posts.jsx
import React, {  PropTypes } from 'react'
import { connect } from 'react-redux';
import {
  creators as PostCrudActions,
  entitySelector as postSelector,
  groupSelector as pagedPosts
} from './post-redux';
 
class PostList extends React.Component {
  componentWillMount() {
    const { getPosts } = this.props;
    getPosts({});
  }
 
  render() {
    const { posts, geting } = this.props;
 
    if(loading) return <div> Loading... </div>;
 
    return (
      <div>
       {
        posts.map(p => (
          <div key={p.id}>
            <Link to={`/post/${p.id}`}>
              {p.title}
            </Link>
          </div>
        )
        )}
      </div>
    )
  }
}
 
PostList.propTypes = {
  posts: PropTypes.arrayOf({
    name: PropTypes.string
  })
};
 
export default connect(state => {
  const pagedData = pagedPosts()(state);
  const posts = pagedData.ids.map(id => postSelector(id)(state));
  return { posts, loading: pagedData.isLoading }
}, 
{
  getPosts: PostCrudActions.getRequest
})(PostList);
 
  
Your State will now look like:

redux store example

Package Sidebar

Install

npm i redux-normalized-crud

Weekly Downloads

4

Version

2.1.1-beta7

License

MIT

Unpacked Size

491 kB

Total Files

48

Last publish

Collaborators

  • sbrigham