# @brigad/redux-rest-easy

Redux/React/React Native framework handling network requests, state management, selectors, caching and much more

[![](https://img.shields.io/circleci/project/github/Brigad/redux-rest-easy/master.svg?style=flat-square\&label=build)](https://circleci.com/gh/Brigad/redux-rest-easy) [![](https://img.shields.io/codecov/c/github/Brigad/redux-rest-easy.svg)](https://codecov.io/gh/Brigad/redux-rest-easy) [![](https://img.shields.io/npm/v/@brigad/redux-rest-easy.svg?style=flat-square)](https://www.npmjs.com/package/@brigad/redux-rest-easy) [![](https://img.shields.io/npm/dt/@brigad/redux-rest-easy.svg?style=flat-square)](https://www.npmjs.com/package/@brigad/redux-rest-easy) [![](https://img.shields.io/npm/l/@brigad/redux-rest-easy.svg?style=flat-square)](https://github.com/Brigad/redux-rest-easy/blob/master/LICENSE.md) [![All Contributors](https://img.shields.io/badge/all_contributors-6-orange.svg?style=flat-square)](#contributors) [![](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) [![](https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square)](https://github.com/Brigad/redux-rest-easy/blob/master/CODE_OF_CONDUCT.md) [![](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) [![](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) [![](https://img.shields.io/github/stars/Brigad/redux-rest-easy.svg?style=social)](https://github.com/Brigad/redux-rest-easy/stargazers)

## [Release article](https://engineering.brigad.co/introducing-redux-rest-easy-6e9a91af4f59)

## Installation

```bash
yarn add @brigad/redux-rest-easy
```

Or, if you are using npm:

```bash
npm install --save @brigad/redux-rest-easy
```

## Problem

At Brigad, we have been extensively using redux and redux-thunk to perform network requests, and store/access the resulting data, and we always felt some pain points, or at least like there were things we could do better:

* We were often **copying/pasting a lot of code** (along with some logic regarding caching, hooks, etc) from one file to another each time we would create a new resource or action
* Our state was **not organized at all**, and accessing it was messy and error-prone
* We had a huge **caching problem**: sometimes performing unnecessary requests, sometimes not performing requests which should have been
* We had no way to know if **a given component was performing an action**, we only knew if an action was being performed on a given resource

## Solution

To solve the problems listed above, `redux-rest-easy` **generates actions, reducers, and selectors**, and also **manages the state's data and metadata** for your **network requests**. It is easy to use, and to observe via the Redux Devtools.

It also provides **sensible defaults**, allowing you to use it with **almost no configuration**, but also to **customize** anything you would like.

And the cherry on the top: it works seamlessly with [redux-offline](https://github.com/redux-offline/redux-offline) and [redux-persist](https://github.com/rt2zz/redux-persist)!

[Scroll down](#minimal-example) for a small example, or [browse the documentation](#api) to get started! To learn more about the problem and solution, you can also read the [release article](https://engineering.brigad.co/introducing-redux-rest-easy-6e9a91af4f59).

## API

```javascript
import {
  createResource,
  reducer,
  connect,
  reset,
  initializeNetworkHelpers,
  getPersistableState,
} from '@brigad/redux-rest-easy';
```

* [createResource](https://brigad.gitbook.io/redux-rest-easy/docs/api/createresource-1) - easily generate then export your actions and selectors from one file
* [reducer](https://brigad.gitbook.io/redux-rest-easy/docs/api/reducer) - plug a single reducer to your state, we handle the rest
* [connect](https://brigad.gitbook.io/redux-rest-easy/docs/api/connect) - connect your components to the state so the magic can happen
* [reset](https://brigad.gitbook.io/redux-rest-easy/docs/api/reset) - reset `redux-rest-easy`'s whole state (you can reset parts of the state with actions generated by `createResource`)
* [initializeNetworkHelpers](https://brigad.gitbook.io/redux-rest-easy/docs/api/initializenetworkhelpers) - provide your own network handlers (optional, fallback to included defaults)
* [getPersistableState](https://brigad.gitbook.io/redux-rest-easy/docs/api/getpersistablestate) - transform the state before storing it, in order to later persist it (using [redux-offline](https://github.com/redux-offline/redux-offline), [redux-persist](https://github.com/rt2zz/redux-persist), or friends)

## Internals

* [Actions configuration](https://brigad.gitbook.io/redux-rest-easy/docs/api/createresource/actionsconfig) - defining your actions with `createResource`
* [Actions](https://brigad.gitbook.io/redux-rest-easy/docs/api/createresource/actions) - actions generated by `createResource`
* [Selectors](https://brigad.gitbook.io/redux-rest-easy/docs/api/createresource/selectors) - selectors generated by `createResource`

## Core principles

1. [Preflight checks](https://brigad.gitbook.io/redux-rest-easy/docs/principles/preflight)
2. [Actions](https://brigad.gitbook.io/redux-rest-easy/docs/principles/actions)
3. [Reducers](https://brigad.gitbook.io/redux-rest-easy/docs/principles/reducers)
4. [Selectors](https://brigad.gitbook.io/redux-rest-easy/docs/principles/selectors)

## Minimal Example

```javascript
// users.js

import { createResource } from '@brigad/redux-rest-easy';

const users = createResource('users')({
  retrieve: {
    method: 'GET',
    url: 'https://my-api.com/users',
    afterHook: () => console.log('Users retrieved successfully'),
  },
});

const {
  actions: { retrieve: retrieveUsers },
  selectors: {
    resource: { getResource: getUsers },
    retrieve: {
      request: { isPerforming: isRetrievingUsers },
    },
  },
} = users;

export { retrieveUsers, getUsers, isRetrievingUsers };
```

```javascript
// reducers.js

import { reducer } from '@brigad/redux-rest-easy';

const reducers = combineReducers({
  restEasy: reducer,
});
```

```javascript
// UsersList.js

import React, { Component } from 'react';
import { connect } from '@brigad/redux-rest-easy';
import {
  retrieveUsers,
  getUsers,
  isRetrievingUsers,
} from './redux-rest-easy/users';

class UsersList extends Component {
  state = {
    error: false,
  };

  componentDidMount() {
    this.props.retrieveUsers(this.onSuccess, this.onError);
  }

  onSuccess = () => {
    this.setState({ error: false });
  };

  onError = () => {
    this.setState({ error: true });
  };

  render() {
    if (this.props.isRetrievingUsers) {
      return <div>{'Loading...'}</div>;
    }

    if (this.state.error) {
      return (
        <div>{'There seems to be a problem... A network error occured.'}</div>
      );
    }

    return <Users items={this.props.users} />;
  }
}

const mapStateToProps = state => ({
  users: getUsers(state),
  isRetrievingUsers: isRetrievingUsers(state),
});

const mapDispatchToProps = dispatch => ({
  retrieveUsers: (onSuccess, onError) =>
    dispatch(retrieveUsers({ onSuccess, onError })),
});

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(ConnectedComponent);
```

## Peer dependencies

Redux-rest-easy assumes you are using [react](https://github.com/facebook/react) (or [react-native](https://github.com/facebook/react-native)) and [react-redux](https://github.com/reactjs/react-redux).

Redux-rest-easy also uses [redux-thunk](https://github.com/gaearon/redux-thunk) under the hood, to handle async actions, and therefore requires you to use redux-thunk's middleware in your store. If you are already using redux-thunk, then you have nothing more to do. Else, follow [redux-thunk's docs](https://github.com/gaearon/redux-thunk#installation) for a quick setup.

## Examples

### [Simple Example](https://codesandbox.io/s/ko7xm5wxy7)

Displays a list of users and allows to create new ones.

### Pagination (TODO, coming soon)

Displays a paginated list, with seamless query-based selectors and cache.

### Data invalidation (TODO, coming soon)

Invalidates store data after a successful POST request

### Multiple requests (e.g. S3 signed upload) (TODO, coming soon)

Performs multiple requests in beforeHook before the final one, to upload a signed file to S3.

### Cache hints (TODO, coming soon)

Makes use of cache hints to customize the built-in cache.

### Store persistence (TODO, coming soon)

Introduces store persistence in the "Pagination" example, so that data persists after the page is refreshed.

## Contributors

Thanks goes to these people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)):

| <p><a href="https://adrien.harnay.me"><img src="https://avatars1.githubusercontent.com/u/15089053?v=4" alt=""><br><strong>Adrien HARNAY</strong></a><br><a href="#blog-adrienharnay">📝</a> <a href="https://github.com/Brigad/redux-rest-easy/commits?author=adrienharnay">💻</a> <a href="https://github.com/Brigad/redux-rest-easy/commits?author=adrienharnay">📖</a> <a href="#ideas-adrienharnay">🤔</a> <a href="#infra-adrienharnay">🚇</a> <a href="#review-adrienharnay">👀</a> <a href="https://github.com/Brigad/redux-rest-easy/commits?author=adrienharnay">⚠️</a></p> | <p><a href="https://github.com/Titozzz"><img src="https://avatars1.githubusercontent.com/u/6181446?v=4" alt=""><br><strong>Thibault Malbranche</strong></a><br><a href="https://github.com/Brigad/redux-rest-easy/issues?q=author%3ATitozzz">🐛</a> <a href="https://github.com/Brigad/redux-rest-easy/commits?author=Titozzz">💻</a> <a href="#ideas-Titozzz">🤔</a> <a href="#review-Titozzz">👀</a></p> | <p><a href="https://github.com/eole1712"><img src="https://avatars3.githubusercontent.com/u/11462388?v=4" alt=""><br><strong>Grisha Ghukasyan</strong></a><br><a href="#ideas-eole1712">🤔</a></p> | <p><a href="https://aymericbeaumet.com"><img src="https://avatars1.githubusercontent.com/u/569243?v=4" alt=""><br><strong>Aymeric Beaumet</strong></a><br><a href="#ideas-aymericbeaumet">🤔</a></p> | <p><a href="https://github.com/Pinesy"><img src="https://avatars3.githubusercontent.com/u/3433722?v=4" alt=""><br><strong>Jess</strong></a><br><a href="https://github.com/Brigad/redux-rest-easy/issues?q=author%3APinesy">🐛</a> <a href="https://github.com/Brigad/redux-rest-easy/commits?author=Pinesy">📖</a></p> | <p><a href="https://github.com/mlabrum"><img src="https://avatars2.githubusercontent.com/u/296106?v=4" alt=""><br><strong>Matt Labrum</strong></a><br><a href="https://github.com/Brigad/redux-rest-easy/commits?author=mlabrum">💻</a></p> |
| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |

This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
