redux-changes
=============

Process changes in redux with path matching.

## Benefits
- easier to reduce data that depends on multiple values in the redux store
- reduce only when changes occur, no needless calculations
- processes all changes within the same reduction preventing breaks normally caused by side effects pattern   
- cleanly decouple reductions based on data that may be affected by multiple actions
- no need to replicate reductions in several action reducers


## Build Status

[![npm version](https://badge.fury.io/js/redux-changes.svg)](https://badge.fury.io/js/redux-changes)<br />
[![Build Status](https://travis-ci.org/sprightco/redux-changes.svg)](https://travis-ci.org/sprightco/redux-changes)<br />
[![NPM](https://nodei.co/npm/redux-changes.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/redux-changes/)


## Install

```js
npm install --save redux-changes
```

## Usage

```js
import { createStore } from 'redux';
import { handleChanges, enableChangeHandling } from 'redux-changes';

const handler = handleChanges({
  'path.in.state.{id}': (state, change, data) => {...}
});

const store = createStore(enableChangeHandling(reducer, handler), initialState)
```

### `handleChange(pathPattern, reducer)`

Creates a reducer that runs if a change is detected in the state at the given path pattern. 


```js
handleChange('counter', (state, change, data) => {
  //change = {
  //  path: string,
  //  value: *,
  //  prevValue: *,
  //  state: {current redux state},
  //  prevState: {previous redux state}
  //}
});
```

### `handleChanges(handlerMap)`

Creates multiple reducers using `handleChange()` and combines them into a single handler that handles multiple changes. Accepts a map where the keys are passed as the first parameter to `handleChange()` (the change path), and the values are passed as the second parameter as the handler.

(Internally, `handleChanges()` works by applying multiple reducers in sequence using [reduce-reducers](https://github.com/acdlite/reduce-reducers).)

Example:

```js
import { createStore } from 'redux';
import { handleChanges, enableChangeHandling } from 'redux-changes';
import { handleActions } from 'redux-actions' 

const reducer = handleActions({
  INCREMENT: (state, action) => ({
    counters: {
      ...state.counters,
      [action.payload]: state.counters[action.payload] + 1
    }
  }),

  DECREMENT: (state, action) => ({
    counters: {
      ...state.counters,
      [action.payload]: state.counters[action.payload] - 1
    }
  })
}, { counters: {}, average: 0 });


const handler = handleChanges({
  'counters.{name}': (state, change) => ({
    return {
      ...state,
      average: calculateAverage(state.counters)
    }
  })
});

const store = createStore(enableChangeHandling(reducer, handler), initialState)
```
