JSPM

  • Created
  • Published
  • Downloads 158
  • Score
    100M100P100Q82992F
  • License MIT

Dispatch reducers

Package Exports

  • repatch

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (repatch) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

Repatch

Dispatch reducers

The most of redux projects do not need sctrict action administration. Action types, action creators and reducers' action handlers are mutually assigned to each other.

The simplest way to keep immutable action controlled dataflow is dispatching pure functions (as reducers) to the store.

So we have only actions which return reducers.

const resolveFetchingUsers = users => state => ({
  ...state,
  users,
  isFetching: false
});

Installation

npm install repatch

How to use

import Store from 'repatch';

const store = new Store(<initialState>);

In CommonJS format you should use:

const Store = require('repatch').default;

Repatch's interface is the same as Redux, therefore you can use with react-redux.

const unsubscribe = store.subscribe(() => console.log(store.getState()));

store.dispatch(resolveFetchingUsers(users));

unsubscribe();

Subreducers

We do not need to reduce always the whole state of the store. A good practice to avoid this effort is using subreducers.

Let's suppose we have the following state:

const store = new Store({
  userManagement: {
    users: [...],
    isFetching: false,
    error: null 
  }
});

Then we can make a subredcer for the userManagement section:

const reduceUserManagement = reducer => state => ({
  ...state,
  userManagement: reducer(state.userManagement)
});

After that reducing only the userManagement state it's easy:

const rejectFetchingUsers = error =>
  reduceUserManagement(state => ({ ...state, error, isFetching: false }));

Middlewares

A repatch middleware takes the store instance and the previous reducer and returns a new reducer:

(Store, Reducer): Reducer

Use addMiddleware method to chaining middlewares:

const store = new Store(<initialState>)
  .addMiddleware(mw1)
  .addMiddleware(mw2, mw3);

Async actions

The thunk middleware is useful for handling async actions similar to redux-thunk.

import Store, { thunk } from 'repatch';

const store = new Store(<initialState>).addMiddleware(thunk);

In thunk async actions reducer returns a function (delegate):

const updateUser = delta => state => async (dispatch, getState) => {
  const editedUserId = getState().editedUser;
  dispatch(toggleSpinner(true));
  await api.updateUser(editedUserId, delta);
  await dispatch(fetchUsers());
  dispatch(toggleSpinner(false));
};

It is possible to embed async actions within each other too and awaiting their resolving:

await dispatch(fetchUsers());

You can access thunk as static member of Store too: Store.thunk

Injecting extra argument

It is possible to inject extra arguments into async actions:

import Store, { thunk } from 'repatch';
import api from './api';
import hashHistory from 'react-router';

const store = new Store(<initialState>)
  .addMiddleware(thunk.withExtraArgument({ api, hashHistory }));

Then you can access these arguments in your delegates:

const updateUser = delta => state =>
  async (dispatch, getState, { api, hashHistory }) => {
    // ...
  }

This way you can keep your async actions independently from outer instances. This practice is useful for testing.

Testing

Sync actions

Sync actions' testing is easy:

import * as assert from 'assert';
import { changeName } from './actions';

// ...

const state = { name: 'hello' };
const nextState = changeName('hi')(state);
assert.strictEqual(nextState.name, 'hi');

Async actions

For async action tests you need to instantiate the Store:

import Store, { thunk } from 'repatch';
import * as assert from 'assert';
import { fetchUsers } from './actions';

const mockUsers = [{ username: 'john' }];
const mockApi = {
  getUsers: () => Promise.resolve(mockUsers)
}

// ...

it('fetchUsers', async () => {
  const state = { users: [] };
  const store = new Store(state)
    .addMiddleware(thunk.withExtraArgument(mockApi));
  await store.dispatch(fetchUsers());
  const nextState = store.getState();
  assert.deepEqual(nextState.users, mockUsers);
});