Redux入門 3日目 Reduxの基本・Reducers(公式ドキュメント和訳)

前回 [Redux入門 2日目 Reduxの基本・Actions](http://qiita.com/kiita312/items/8f8d047e5cbd87399ccb)

前回に引き続きTodoアプリの作成を通してReduxの要素の解説を行ないます。今回はReducerです。

## 2.2 Reducers

reducerは、actionを受けてstateを変更するの為のメソッドです

### Designing the State Shap (Stateのデータ設計)
* Reduxでは、stateはすべて個別のオブジェクトとして保持されます

今回のTodoアプリでは２つの内容をstateで保持します

1. 現在選択されている表示/非表示

2. todoのリスト


```js
{
  visibilityFilter: 'SHOW_ALL',
  todos: [
    {
      text: 'Consider using Redux',
      completed: true,
    },
    {
      text: 'Keep all state in a single tree',
      completed: false
    }
  ]
}
```
stateにはUIの内容を入れないようにするのが推奨されています

### Handling Actions (actionの制御)
reducerは、現在のstateとactionを受けて新しいstateを返すだけの純粋なメソッドです。

```js
(previousState, action) => newState
```

reducerの中で以下のことをやってはいけません

* 引数のstate, actionインスタンスの値を変更する
* 副作用をおこす(APIを呼んだり、ルーティングを変えるなどなど)
* 毎回値が変わるもの(Date.now() や Math.random())を扱う


ここからReducerを書いていきます。
Reduxでは最初にreducerはstateがundefinedで呼び出します。その際に初期値を設定します。

```js
import { VisibilityFilters } from './actions'

const initialState = {
  visibilityFilter: VisibilityFilters.SHOW_ALL,
  todos: []
}

function todoApp(state, action) {
  if (typeof state === 'undefined') {
    return initialState
  }

  // For now, don’t handle any actions
  // and just return the state given to us.
  return state
}
```
ES6ではもっと簡潔に記述できます

```js
function todoApp(state = initialState, action) {
  // For now, don’t handle any actions
  // and just return the state given to us.
  return state
}
```

actionがSET_VISIBILITY_FILTERの時の処理を追加します

```js
function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    default:
      return state
  }
}
```

* Object.assignを使用してstateのコピーを作成しています。Object.assign(state, { visibilityFilter: action.filter })とすると、stateを変更することになってしまうのでやってはいけません。空のオブジェクトにマージさせないといけません。

※ Object.assignはES6の仕様ですがまだ実装されているブラウザは多くありません。polyfillかBabelを使用してください

* stateが不明の場合は、stateをそのまま返すのがセオリーです

reducerで制御するactionを追加します

```js
function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    case ADD_TODO:
      return Object.assign({}, state, {
        todos: [
          ...state.todos,
          {
            text: action.text,
            completed: false
          }
        ]
      })
    case COMPLETE_TODO:
      return Object.assign({}, state, {
    	todos: [
      	  ...state.todos.slice(0, action.index),
      	  Object.assign({}, state.todos[action.index], {
            completed: true
          }),
          ...state.todos.slice(action.index + 1)
        ]
      }) 
    default:
      return state
  }
}
```
* COMPLETE_TODOでは、stateを変更しないようにsliceを使って順番を変更した新しい配列を作成しています。

## Splitting Reducers（Reducersを分割）

reducer内でのvisiblity_filterとtodosの処理を子reducerとして分割すると分かりやすくなります。
初期値の値の処理はそれぞれの子reducerに記述します。
これはreducer compositionと呼ばれていて、Reduxアプリケーションの基本的な構成です。

```js
function todos(state = [], action) {
  switch (action.type) {
    case ADD_TODO:
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case COMPLETE_TODO:
      return [
        ...state.slice(0, action.index),
        Object.assign({}, state[action.index], {
          completed: true
        }),
        ...state.slice(action.index + 1)
      ]
    default:
      return state
  }
}

function visibilityFilter(state = SHOW_ALL, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return action.filter
    default:
      return state
  }
}

function todoApp(state = {}, action) {
  return {
    visibilityFilter: visibilityFilter(state.visibilityFilter, action),
    todos: todos(state.todos, action)
  }
}
```

最後に、ReduxではcombineReducers()というユーティリティを提供しており、todoAppを書き換えることができます。combineReducerでは分割された子reducer名と同じキーのstateが使用されます。

```js
import { combineReducers } from 'redux'

const todoApp = combineReducers({
  visibilityFilter,
  todos
})

export default todoApp
```
続き [Redux入門 4日目 Reduxの基本・Stores](http://qiita.com/kiita312/items/377787c24efac64f2495)