Redux ExampleのTodo Listをはじめからていねいに(2)

[Reduxの公式ExampleにあるTodo List](http://redux.js.org/docs/basics/ExampleTodoList.html)を機能ごとに作っていくシリーズの2回目です。

1回目では、TodoをTodo Listに追加する「Add Todo」を作りました。
今回は、Todoの完了・未完了を切り替える「Toggle Todo」の機能を作っていきます。

1回目を読んでない人は、そちらを先にどうぞ。
[Redux ExampleのTodo Listをはじめからていねいに(1)](http://qiita.com/xkumiyu/items/9dfe51d2bcb3bdb06da3)

# 1. 完了・未完了を表すcompletedによってスタイルを変える

## todoにcompleted要素を追加して、とりあえず取り消し線を表示する

まず、todoごとに完了・未完了を区別するために、completedという要素を
加えます。前回つくったtodo reducerを修正します。
todo作成時は、未完了なので、デフォルトでfalseにしておきます。

デフォルトでfalseにするので、actionからなにか受け取る必要はありませんので、
addTodoのactionCreatorは変更ありません。

``` reducers/todos.js
const todo = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return {
        id: action.id,
        text: action.text,
        completed: false
      }
    default:
      return state
  }
}

const todos = (state = [], action) => {
  // ...
}
export default todos
```

completedによってviewを変えるので、Todoコンポーネントを修正します。
completedがtrueだったらtextDecorationをline-throughにします。

``` components/Todo.js
const Todo = ({ completed, text }) => (
  <li style={{textDecoration: completed ? 'line-through' : 'none'}}>
    {text}
  </li>
)

Todo.propTypes = {
  completed: PropTypes.bool.isRequired,
  text: PropTypes.string.isRequired
}
```

これで、stateで保持されるtodoのcompletedがtrueのとき取り消し線がつきます。
動作確認は、一時的にreducers/todos.jsのcompleted: falseを
trueに変えてやればちゃんと取り消し線が付いているはずです。

## actionCreatorからcompleted要素を操作する

次に、action経由で取り消し線のON/OFFを行うために、actionCreatorとreducerの作成を行います。
actionCreatorで必要なのは、todoのidだけです。

``` actions/index.js
export const addTodo = (text) => {
  // ...
}

export const toggleTodo = (id) => {
  return {
    type: 'TOGGLE_TODO',
    id
  }
}
```

reducerはtodosとtodoの両方にtoggleTodoのactionが呼び出されたときの処理が必要です。
storeにはtodos reducerが登録されており、todoはtodosが呼び出されているに過ぎません。

todos reducerでは、map関数を使って現在のtodosに格納されているすべてのtodoを
todo reducerに渡しています。
todo reducerでは、actionCreatorに渡したidと一致するtodoに対して、
completedだけを反転させています。
[Object.assign](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)で現在のstateと、completedを書き換えたstateを結合しています。

``` reducers/todos.js
const todo = (state, action) => {
  switch (action.type) {
    // ...
    case 'TOGGLE_TODO':
      if (state.id !== action.id) {
        return state
      }
      return Object.assign({}, state, {
        completed: !state.completed
      })
    // ...
  }
}

const todos = (state = [], action) => {
  switch (action.type) {
    // ...
    case 'TOGGLE_TODO':
      return state.map((t) =>
        todo(t, action)
      )
    // ...
  }
}
export default todos
```

index.jsからtoggleTodoを使ってみると、
正しく取り消し線が付いていると思います。

``` index.js
import { addTodo, toggleTodo } from './actions'

store.dispatch(addTodo('Hello React!'))
store.dispatch(toggleTodo(0))
```

# 2. クリックしてcompletedの値を変える

それでは、クリックしたときにcompletedの値を変更する処理を書いていきます。

stateをpropsとして使えるようにしたと同じように、dispatchをpropsとして
使えるようにします。

onTodoClickという名前でdispatchをstoreに格納します。
idを渡すと、dispatch(toggleTodo(id))のようにtoggleTodo actionCreatorで
actionをつくって、dispatchによりstoreのstateを変更します。
処理の流れは今までと同じです。

``` containers/VisibleTodoList.js
import { toggleTodo } from '../actions'
// ...
const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

export default VisibleTodoList
```

TodoListコンテナでonTodoClickが使えるようになったので、Todoコンテナを
つくるところで、他のpropsと同じようにonTodoClick(todo.id)も渡します。

``` components/TodoList.js
const TodoList = ({ todos, onTodoClick }) => (
  <ul>
    {todos.map((todo) =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => onTodoClick(todo.id)}
      />
    )}
  </ul>
)

TodoList.propTypes = {
  // ...
  onTodoClick: PropTypes.func.isRequired
}

export default TodoList
```

TodoコンテナでTodoListから渡されたonClickを使います。

``` components/Todo.js
const Todo = ({ onClick, completed, text }) => (
  <li
    onClick={onClick}
    style={{textDecoration: completed ? 'line-through' : 'none'}}
  >
    {text}
  </li>
)

Todo.propTypes = {
  onClick: PropTypes.func.isRequired,
  // ...
}

export default Todo
```

これでクリックするとcompletedの値が変更され、取り消し線がON/OFFされます。

![Untitled.gif](https://qiita-image-store.s3.amazonaws.com/0/35980/39b2e83e-f890-b689-7030-57fc6d384975.gif)


「Toggle Todo」機能が完成しました。
ここまでのソースコードは[GitHub](https://github.com/xkumiyu/redux-todos/tree/toggle-todo)にあげています。

続きます。。
次回、表示するTodo Listを完了または未完了のTodoだけにする「Filter Todo」機能を実装します。

**2016/3/15 update**
[Redux ExampleのTodo Listをはじめからていねいに(3)](http://qiita.com/xkumiyu/items/1ba476b8043b71561f52)を書きました。