UNPKG

redux-shelf

Version:

Helper reducers and actions to take away your boilerplate code from both Data and Communication state types

238 lines (188 loc) 6.55 kB
# Redux Shelf Redux Shelf is a library that provide a simple API to create and manage both **Entity** and **Communication** state of your application using Redux reducers, by managing for you all the boilerplate necessary to do that. [![Build Status](https://travis-ci.org/lemes/redux-shelf.svg?branch=master)](https://travis-ci.org/lemes/redux-shelf) ## Influences React applications contains several types of state wich we have to manage. James K. Nelson, on this [great article](http://jamesknelson.com/5-types-react-application-state/), identify 5 types of them. On this library we are focused on two of the 5 types: Data state, that we prefer to call Entity state, and Communication state. Below you can find a summary of each type: ### Data State > "Data state covers information which your application temporarily stores about the big wide world. > That is, it covers your business data." > > "_Every piece of received Data has a **type**, and a **selector** which exactly specifies which > data was received._" ### Communication State > "This type of state covers the seemingly simple yet somewhat thorny information which represents > things like loading spinners and error messages." > > "_Communication state is the status of any not-yet-complete requests to other services._" > > "This means that all of the following are communication state: > > * The type/selector for any Data you expect to receive > * The type, selector and expected change of any operations you have requested on Data > * The error messages for anything which didn’t go quite as planned." With Redux Shelf you can reduce Entity and Communication state management boilerplate in your application, by using a simple and easy to learn API. ## Installation `yarn add redux-shelf` or `npm install redux-shelf` ## Configuration ```javascript // reducers/index.js import { combineReducers } from 'redux'; import { entities, communication } from 'redux-shelf'; export default (appReducers = combineReducers({ entities, communication, })); ``` ## API Bellow you can find the description of the API provided by Redux Shelf library. ### Entity * `set(type, payload)`: Overrides the current state of an Entity. * `update(type, payload)`: Merge the current state of an Entity with new state. * `remove(type, selector)`: Remove a record of an Entity. * `idsOf(type)`: Returns the array of ids of an Entity type provided as parameter. * `contentOf(type, selector)`: Returns content object of an specific Entity record, identified by its type and selector provided as parameters. * `of(type, selector?)`: It's an alias for `idsOf` and `contentOf` methods. When only `type` parameter is given to `of` method it behaves like `idsOf` call, while when `selector` parameter is also provided `of` method will behave like `contentOf` call. <span style="color: red">Note: </span>By using Entity Actions API we're assuming that you'll normalize Entity data on ids/content form. So, you **must** either use `normalize` function provided by library or use another one that works similarly (check _Utils_ section). ### Communication * `starting(type, selector?)`: Sets communication status with the `STARTING` status for the given entity type and selector. * `done(type, selector?)`: Sets communication status with the `DONE` status for the given entity type and selector. * `fail(type, selector|error, error)`: Sets communication status with the `FAIL` status for the given entity type, selector and/or error. * `cancel(type, selector?)`: Sets communication status with the `CANCEL` status for the given entity type and selector. * `of(type, selector?)`: Returns an object with `loading` and `error`. ### Utils * `normalize(payload, key?)`: Normalizes a given payload to ids/content shape. If `key` parameter is not provided, the function will normalize the payload by _id_ property, assuming that it has it. The valid values for _payload_ parameter are: An object or an array of objects. If the value provided as _payload_ parameter is invalid, the function will return a default normalized object `{ ids: [], content: {} }`. See the examples below: ```javascript const payload = [{ id: 1, name: 'Product 1' }, { id: 2, name: 'Product 2' }]; console.log(normalize(payload, 'id')); // console output /* { ids: [1, 2], content: { 1: { id: 1, name: 'Product 1' }, 2: { id: 2, name: 'Product 2' }, }, } */ ... const payload = { id: 1, name: 'Product 1' }; console.log(normalize(payload)); // console output /* { ids: [1], content: { 1: { id: 1, name: 'Product 1' }, }, } */ ... const payload = [ { identifier: 1, name: 'Product 1' }, { identifier: 2, name: 'Product 2' }, { id: 3, name: 'Product 2' }, true, null, 42, ]; console.log(normalize(payload, 'identifier')); // console output /* { ids: [1, 2], content: { 1: { identifier: 1, name: 'Product 1' }, 2: { identifier: 2, name: 'Product 2' }, }, } */ ``` ## Usage ```javascript // userActions.js import { entities, communication, normalize } from 'redux-shelf'; // Here I assuming that you're using some middleware to handle // asynchronous actions, for example, Redux Thunk export function fetchUsers() { return async (dispatch) => { dispatch(communication.starting('users')); try { const url = 'endpoint_to_get_users_data'; const request = await fetch(url); const payload = request.json(); dispatch(entities.set('users', normalize(payload))); dispatch(communication.done('users')); } catch (e) { dispatch(communication.fail('users', e)); console.log(e); } }; } ... // UserList.jsx export const UserList = ({ loading, error, userIds }) => { if (error) { return <div>Failed to load users</div> } if (loading) { return <div>Loading...</div> } return ( <div> {userIds.map(userId => <UserItem key={userId} userId={id} />)} </div> ); ); export default connect( ({ entities, communication }) => { const { loading, error, } = communication.of('users'); const userIds = entities.idsOf('users'); return { loading, error, userIds, }; }, )(UserList); ... // UserItem.jsx export const UserItem = ({ name }) => ( <span> {name} </span> ); export default connect( ({ entities }, { userId }) => { const user = entities.contentOf('users', userId); return { name: user.name, }; }, )(UserItem); ``` ## License MIT