Skip to content

bratushka/redux-from-to

Repository files navigation

Build Status codecov

redux-from-to

redux-from-to carries resources from APIs to your redux store.

Stop writing the same old action and reducer logic and DRY up your redux application.

Quick start

Install

$ npm install redux-from-to

or

$ yarn add redux-from-to

Wrap your root reducer:

// store.js or wherever you create your store.

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { wrapper } from 'redux-from-to';

import rootReducer from './rootReducer'


const store = createStore(
  wrapper(rootReducer),
  applyMiddleware(thunk),
)

You're all set!

API

fromTo(from, to, [through, [options]])

CAUTION: for now, fromTo works only with state stored as Immutable iterables.

Makes the API call, dispatches REQUEST and FAILURE or SUCCESS actions, and populates your store with response data.

Arguments:

from (Function)

A function returning a Promise.

to (Array or Object)

The path to the target location in your store for data returned by from.

If to is an Array, fromTo will automatically create

{
  isRequesting: (boolean),  // true if `from` has not resolved/rejected
  error: (any),             // data returned by `from` rejecting, if any
  data: (any),              // data returned by `from` resolving, if any
}

at the target location.

If you want to specify different locations for these three, pass an object like

{
  request: (Array<any>),
  failure: (Array<any>),
  success: (Array<any>),
}

If any of the locations is not specified - it will be ignored in store updating. Example: if request is omitted - the state will not be populated with isRequesting property.

[through] (Object)

Contains adapters to transform data returned by from before it is saved in the store at to.

Looks like

{
  errorAdapter: (Function),     // transforms data returned by `from` rejecting, if any
  responseAdapter: (Function),  // transforms data returned by `from` resolving, if any
}

Adapters receive state as second argument.

Note: This library was written for using with axios, so the default adapters fit axios. You can see these in ./src/from-to.js.

[options] (Object)

Contains additional parameters, which are not frequently used. Currently only action property is accepted. In case action is specified - only this action will be dispatched.

Example:

import axios from 'axios';
import { expect } from 'chai';
import { fromTo, actions as fromToActions } from 'redux-from-to';

import myStoreConstructor from './myStoreConstructor';

function fetchUser(action) {
  return fromTo(
    () => axios.get('http://localhost/me'),
    ['user'],
    undefined,
    { action },
  );
}

describe('fetchUser', () => {
  it('should have `isRequesting` flag while fetching', () => {
    const store = myStoreConstructor();
    
    return store.dispatch(fetchUser(fromToActions.request)).then(() => {
      const actual = store.getState().user.get('isRequesting');
      
      expect(actual).to.be.true;
    });
  });
})

Simple usage

Dispatch fromTo

// axios call returns {"mood": "happy"} as response body, 200 as status

dispatch(fromTo(
  () => axios.get('dogs.io/good-boy'),
  ['dogs', 'goodBoy'],
));

State before

{
  ...otherData,
  dogs: Immutable.fromJS({}),
}

State before from resolves/rejects

{
  ...otherData,
  dogs: Immutable.fromJS({
    goodBoy: {
      isRequesting: true,
    },
  }),
}

State if from rejects

{
  ...otherData,
  dogs: Immutable.fromJS({
    goodBoy: {
      isRequesting: false,
      error: {
        data: { not: 'found' },
        status: 404,
      },
    },
  }),
}

State if from resolves

{
  ...otherData,
  dogs: Immutable.fromJS({
    goodBoy: {
      isRequesting: false,
      data: {
        mood: 'happy',
      },
    },
  }),
}

Power usage

Dispatch fromTo

// axios call returns {results: {"mood": "happy"}} as response body, 200 as status

dispatch(fromTo(
  () => axios.get('dogs.io/good-boy'),
  {
    request: ['dogs', 'goodBoy'],
    failure: ['errors', 'goodBoy'],
    success: ['requests', 'goodBoy'],
  },
  {
    errorAdapter: error => `The error is ${error}`,
    responseAdapter: response => response.data.results,
  },
));

State before

{
  ...otherData,
  dogs: Immutable.fromJS({}),
  errors: Immutable.fromJS({}),
  requests: Immutable.fromJS({}),
}

State before from resolves/rejects

{
  ...otherData,
  dogs: Immutable.fromJS({}),
  errors: Immutable.fromJS({}),
  requests: Immutable.fromJS({
    goodBoy: true,
  }),
}

State if from rejects

{
  ...otherData,
  dogs: Immutable.fromJS({}),
  errors: Immutable.fromJS({
    goodBoy: 'The error is someError',
  }),
  requests: Immutable.fromJS({
    goodBoy: false,
  }),
}

State if from resolves

{
  ...otherData,
  dogs: Immutable.fromJS({
    goodBoy: {
      mood: 'happy',
    },
  }),
  errors: Immutable.fromJS({}),
  requests: Immutable.fromJS({
    goodBoy: false,
  }),
}

About

Redux state manager for handling promises

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published