Create functional, reusable redux reducers. Liberate yourself from switch.
(g∘f) Composable: reducers are just plain functions. Nest and compose them with other reducers and libraries.
♺ Reusable: designed for redux but flexible enough to use elsewhere. Create building blocks and reuse them.
xⁿ Powerful: comes with a utility belt for working with redux actions.
npm install reducer-redux
An example from the redux tutorial:
import match from 'reducer-redux';
import { combineReducers } from 'redux';
import { ADD_TODO, TOGGLE_TODO, SET_VISIBILITY_FILTER, VisibilityFilters } from './actions';
const visibilityFilter = match.withDefault(VisibilityFilter.SHOW_ALL)(
    match.plainAction({ type: SET_VISIBILITY_FILTER })
        .with(action => action.filter)
)
const todos = match.withDefault([])(
    match.first(
        match.plainAction({ type: ADD_TODO })
            .with((action, state) => [
                ...state,
                { 
                    text: action.text,
                    completed: false,
                }
            ]),
        match.plainAction({ type: TOGGLE_TODO })
            .with((action, state) => state.map(
                match((todo, index) => index === action.index)
                    .with(todo => ({ ...todo, completed: !todo.completed }))
            )),
    ))
export default combineReducers({
    visibilityFilter,
    todos
});
The library exports a function called match. match returns functions called Matchers, which
are the core abstraction of library. A Matcher is a tuple of (condition, reducer). The condition
is a predicate, and the reducer is any function.
Matchers created with match don't have a reducer yet. You specify a reducer by calling the
Matcher's .with() function.
When the Matcher is called with some arguments, it first calls the condition.
- If the 
conditionreturns true, the matcher returns the result of thereducerwith the same arguments. - __If the 
conditionreturns false, the matcher returns the first argument. 
Here's an example:
const matcher = match(value => value === 'foo')
    .with(() => 'bar')
matcher('foo') // 'bar'
matcher('bar') // 'foo'
matcher('bar', 'baz') // 'foo'
Although the library is powerful enough for use anywhere, it's designed for redux.
The reducer of a Matcher returns the first argument (the state) if the condition returns
false.
This property enables us to specify some conditions for which to modify a store state,
and the Matcher will leave the state unchanged for any other condition!
Here's an example:
const counter = match((state, action) => action.type === 'increment')
    .with((state, action) => state + 1)
counter(0, { type: 'increment' }) // 1    
counter(0, { type: 'something else' }) // 0    
counter(0, { type: 'another action' }) // 0    
Of course, a redux application needs to handle more than one action type.
reducer-redux comes with a utility to combine reducers: match.first().
match.first() takes a group of reducers and uses the first one whose condition returns true.
Here's an example:
const counter = match.first(
    match((state, action) => action.type === 'increment')
        .with((state, action) => state + action.amount),
    match((state, action) => action.type === 'decrement')
        .with((state, action) => state - action.amount)
)
counter(1, { type: 'increment', amount: 1 }) // 2    
counter(1, { type: 'decrement', amount: 2 }) // -1    
counter(1, { type: 'another action' }) // 1