combineReducersが辛い

## はじめに
`combineReducers`大好きです。Stateの形と直結した直感的なやり方でReducerを分割できてすごくわかりやすいです。
ただ別のStoreじゃないかってレベルで外側のStateにアクセス出来ないので、そういう時は巨大なReducerを作るはめになることがあります。

## 巨大なReducer
例えば、これはいまのところ巨大なReducerではありませんが、将来的にそうなりうるState構造とそのReducerの例です。
ここで先に言っておくと、この例はActionをDispatchする側が`currentTodoKey`を取得していないという状況があまり想像できないので、もしかするとあまり良い例ではないのかもしれません。

```js
// State
{
  todos: {
    todoA: [],
    todoB: [],
    todoC: []
  },
  currentTodoKey: "todoA"
}

// Reducer
const reducer = (state = { todos: {}, currentTodoKey: "default" }, action) => {
  const addTodo = (state, key, todo) {
    return Object.assign({}, state, {
      todos: Object.assign({}, state.todos, {
        [key]: (state.todos[key] || []).concat(todo)
      })
    })
  }

  switch (action.type) {
    case 'ADD_TODO':
      var { key, todo } = action.payload
      return addTodo(state, key, todo)
    case 'ADD_TODO_TO_CURRENT_KEY':
      var { todo } = action.payload
      return addTodo(state, state.currentTodoKey, todo)
    case 'UPDATE_CURRENT_KEY':
      var { newKey } = action.payload
      return Object.assign({}, state, {
        currentTodoKey: newKey
      })
    default:
      return state
  }
}
```
要するにこの例では何が言いたいのかというと、`combineReducers`を使ってこのReducerを２つのReducer`todos`と`currentTodoKey`に分けたいが、`todos`のReducerが外部の`currentTodoKey`を必要とするためにそれが不可能であるということです。
そして、この記事の目的はこれを**combineReducersライクな方法で分ける**ことです。

## Section Reducerとは
ここで、僕が勝手に`Section Reducer`と読んでいるものを使って分割を進めていきます。
Section ReducerはReducerの
・`(previousState, action) => nextState`
このような定義に対して、以下の様なものになります。
・`(previousSectionState, action, state) => nextSectionState`
ここで使われている`previous/nextSectionState`はこの例で言う`todos`, `currentTodoKey`に相当するものです。
また`combineReducers`のやり方に合わせて、以下のように表すこともあります。
・`(previousState, action, entireState) => nextState`
注意して欲しいのは`Section Reducer`は巨大なReducerを小さな単位に分けるためのただの道具であって、それ以外の何者でもないということです。

## Section Reducerの適用
それでは、早速先程の例に`Section Reducer`のやり方を適用してみます。

```js
const todos = (state = {}, action, entireState) => {
  switch (action.type) {
    case 'ADD_TODO':
      var { key, todo } = action.payload
      return Object.assign({}, state, {
        [key]: (state[key] || []).concat(todo)
      })
    case 'ADD_TODO_TO_CURRENT_KEY':
      var { todo } = action.payload
      var key = entireState.currentTodoKey
      return Object.assign({}, state, {
        [key]: (state[key] || []).concat(todo)
      })
    default:
      return state
  }
}

const currentTodoKey = (state = "default", action) => {
  switch (action.type) {
    case 'UPDATE_CURRENT_KEY':
      var { newKey } = action.payload
      return newKey
    default:
      return state
  }
}

const reducer = (state, action) => ({
  todos: todos(state.todos, action, state),
  currentTodoKey: currentTodoKey(state.currentTodoKey, action)
})
```
無事に分割することに成功しました。

## combineSectionReducersを使う。
上の例で示したこの部分ですが毎回書くのは少し面倒くさそうです。

```js
const reducer = (state, action) => ({
  todos: todos(state.todos, action, state),
  currentTodoKey: currentTodoKey(state.currentTodoKey, action)
})
```
また`combineReducers`がしてくれるように、それぞれのReducerが正しいReducerかどうか確かめたい時もあるかもしれません。
そこで、私がnpmに公開している`combineSectionReducers`というパッケージを利用して、以下のように書き換えます。

```js
import combineSectionReducers from 'combine-section-reducers'

const reducer = combineSectionReducers({
  todo, currentTodoKey
})
```
これで、この記事の目標は達成できました。

## combineSectionReducersについて
もちろん`combineSectionReducers`は`combineReducers`のように深くネストして使うことができます。
また`combineReducers`によって作られたState木の一部分にだけ`combineSectionReducers`を適用するといったこともできます。
それは`combineSectionReducers`の返す関数がただのReducerとしても`Section Reducer`としても使うことができるようになっているためです。

## さいごに
combineReducers大好きです。

あと、この記事に載せたコードの動作確認は行っていません。

## リンク
NPM: [![npm version](https://badge.fury.io/js/combine-section-reducers.svg)](https://badge.fury.io/js/combine-section-reducers)
GitHub: [ryo33/combine-section-reducers](https://github.com/ryo33/combine-section-reducers)