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

More than 1 year has passed since last update.

Reduxの公式ExampleにあるTodo Listを機能ごとに作っていくシリーズの2回目です。

1回目では、TodoをTodo Listに追加する「Add Todo」を作りました。

今回は、Todoの完了・未完了を切り替える「Toggle Todo」の機能を作っていきます。

1回目を読んでない人は、そちらを先にどうぞ。

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


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で現在の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

「Toggle Todo」機能が完成しました。

ここまでのソースコードはGitHubにあげています。

続きます。。

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

2016/3/15 update

Redux ExampleのTodo Listをはじめからていねいに(3)を書きました。