HTML5テクニカルノート
React + Redux入門 03: 項目の処理済みと未処理でスタイルを変える
- ID: FN1908003
- Technique: HTML5 / ECMAScript 2015
- Library: React 16.8.6
「React + Redux入門 02: フィールドに入力したテキストを項目リストに加える」で、リストに項目が追加できるようになりました。Todoリストですから、項目が済んだかどうかのチェックをつけ、スタイルが変わるようにしましょう。
01 ActionとReducer
チェックボックスを使ってもよいのですが、つぎのような絵文字で示すことにします。
👋 未処理
👌 処理済み
項目が未処理か処理済みかも、もちろん状態としてStoreにもたせます。まずつくるのは、Action Creatorです。モジュールsrc/actions/index.js
に、つぎのようにtoggleTodo
を定めてください。引数に受け取ったid
を、Actionオブジェクトに納めて返します。
src/actions/index.jsexport const toggleTodo = (id) => ({ type: 'TOGGLE_TODO', id });
このActionオブジェクトを送るのは、クリックされて処理/未処理が切り替わる項目(Todo
)です。けれど、そのための関数(onClick
)は、つぎのように親のモジュールsrc/components/TodoList.js
に定めて、テンプレートで子コンポーネントに与えます。
ActionオブジェクトをStoreに送るには、connect()
関数の第2引数に関数(mapStateToProps()
)を渡さなければなりません(「Connect: Dispatching Actions with mapDispatchToProps」参照)。戻り値は、dispatch()
関数を引数に受け取り、ActionオブジェクトをStoreに送る関数です。
src/components/TodoList.jsimport { toggleTodo } from '../actions'; // const TodoList = ({ todos }) => ( const TodoList = ({ todos, toggleTodo }) => ( <ul className="todo-list"> {todos.map((todo) => <Todo onClick={() => toggleTodo(todo.id)} /> )} </ul> ); const mapDispatchToProps = (dispatch) => ({ toggleTodo: (id) => dispatch(toggleTodo(id)) }); export default connect( mapStateToProps, mapDispatchToProps )(TodoList);
モジュールsrc/reducers/todos.js
に加えるReducerは、つぎのとおりです。チェックが切り替わる項目id
をActionから受け取って、そのデータのプロパティcompleted
の論理値を反転します。
src/reducers/todos.jsconst todos = (state = [], action) => { switch (action.type) { case 'TOGGLE_TODO': return state.map((todo) => (todo.id === action.id) ? {...todo, completed: !todo.completed} : todo ) } };
項目のモジュールsrc/components/Todo.js
は、つぎのように引数オブジェクトから取り出すプロパティが増えます。onClick
属性に与えるのは、親コンポーネントから受け取った関数(onClick
)です。処理済みかどうかのプロパティ(completed
)は、加えるチェックマークとstyle
属性の切り替えに使います。
src/components/Todo.js// const Todo = ({ text }) => ( // <li> const Todo = ({ onClick, completed, text }) => ( <li onClick={onClick} style={{ textDecoration: completed ? 'line-through' : 'none' }} > {completed ? "👌" : "👋"}{" "} </li> );
これで、リスト項目をクリックしたとき、チェックサインのオン/オフが切り替わるようになります(図001)。
図001■項目をクリックするとチェックサインが切り替わる
02 要素のクラスを切り替える
ここで、src/App.css
を、つぎのコード001のように書き替えてください。
コード001■src/App.css
.todo-list {
margin-top: 1rem;
text-align: left;
list-style: none;
}
.todo-item {
font-family: monospace;
cursor: pointer;
line-height: 1.5;
}
.todo-item__text--completed {
text-decoration: line-through;
color: lightgray;
}
実は、クラスtodo-listは、すでにモジュールsrc/components/TodoList.js
のリスト(<ul>
要素)に割り当ててありました。
src/components/TodoList.jsconst TodoList = ({ todos, toggleTodo }) => ( <ul className="todo-list"> </ul> );
項目テキストのクラスがtodo-item
です。そして、処理済み項目には、todo-item__text--completed
を与えます。モジュールsrc/components/Todo.js
に、つぎのように書き加えればよいでしょう。
src/components/Todo.jsconst Todo = ({ onClick, completed, text }) => ( <li /* style={{ textDecoration: completed ? 'line-through' : 'none' }} */ className={'todo-item' + (completed ? ' todo-item__text--completed' : '') } > </li> );
03 Classnamesで要素のクラスを定める
複数のクラス名を組み合わせてclassName
属性に与えたいときに使えるユーティリティがClassnamesです。引数にはクラス名をいくつでも渡せます。値は文字列またはオブジェクトです。クラス名をプロパティにしたオブジェクトにすると、値がtrue
のときに適用されます。npmでインストールしましょう。
src/components/Todo.jsnpm install classnames
src/components/Todo.js
のテンプレートの項目テキスト(<span>要素)にclassName
属性を加えたのが、つぎのコードです。Classnamesの関数(classNames()
)により、項目のcompleted
プロパティがtrue
のときに、処理済み用のクラス(todo-item__text--completed
)が割り当てられます。
src/components/Todo.jsimport classNames from 'classnames'; const Todo = ({ onClick, completed, text }) => ( <li className={classNames( "todo-item", {"todo-item__text--completed": completed} )} > </li> );
なお、React Redux公式サイトの「Basic Tutorial」でつくるTodoリストにも、少しコンポーネントの組み立ては違うものの、Classnamesが用いられています。ただし、関数の引数はつぎのような書き方です(「Todo App with Redux」参照)。論理演算子を条件判定に使っており、少しわかりにくいきらいがあります(「if文なしに論理演算子で条件判定の処理をする」)。また、前項02のように条件演算子?:
で書くのと、さほどかわりばえしません。上記のようにオブジェクトで値に条件を与える方がわかりやすいでしょう。
className={classNames( "todo-item", todo && todo.completed && "todo-item__text--completed" )}
これで、チェックをつけた処理済み項目のテキストは、クラスの追加によりスタイルが変わります(図002)。
図002■処理済み項目のスタイルが変わる
05 Actionのtypeを定数として定める
Actionのtype
は、Action Creatorのほか、Reducerでも使います。数が増えることにも備えて、文字列のままでなく定数にした方が便利でしょう。その変更を加えたAction CreatorとReducerのふたつのモジュールのコード全体は、以下のコード002と003のとおりです。
コード002■src/actions/index.js
export const ADD_TODO = 'ADD_TODO';
export const TOGGLE_TODO = 'TOGGLE_TODO';
let nextTodoId = 0;
export const addTodo = (text) => ({
type: ADD_TODO,
id: nextTodoId++,
text
});
export const toggleTodo = (id) => ({
type: TOGGLE_TODO,
id
});
コード003■src/reducers/todos.js
import { ADD_TODO, TOGGLE_TODO } from '../actions'
const todos = (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return [
...state,
{
id: action.id,
text: action.text,
completed: false
}
];
case TOGGLE_TODO:
return state.map((todo) =>
(todo.id === action.id)
? {...todo, completed: !todo.completed}
: todo
)
default:
return state;
}
};
export default todos;
そして、書き替えたコンポーネントのモジュールについては、以下のコード004および005に全体をまとめます。
コード004■src/components/TodoList.js
import React from 'react';
import { connect } from 'react-redux';
import { toggleTodo } from '../actions';
import Todo from './Todo';
const TodoList = ({ todos, toggleTodo }) => (
<ul className="todo-list">
{todos.map((todo) =>
<Todo
key={todo.id}
{...todo}
onClick={() => toggleTodo(todo.id)}
/>
)}
</ul>
);
const mapStateToProps = (state) => ({
todos: state.todos
});
const mapDispatchToProps = (dispatch) => ({
toggleTodo: (id) => dispatch(toggleTodo(id))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(TodoList);
コード005■src/components/Todo.js
import React from 'react';
import classNames from 'classnames';
const Todo = ({ onClick, completed, text }) => (
<li
onClick={onClick}
className={classNames(
"todo-item",
{"todo-item__text--completed": completed}
)}
>
{completed ? "👌" : "👋"}{" "}
<span>
{text}
</span>
</li>
);
export default Todo;
サンプル001■react-redux-todos-03
React + Redux入門
- React + Redux入門 01: テキスト入力のフォームをつくる
- React + Redux入門 02: フィールドに入力したテキストを項目リストに加える
- React + Redux入門 03: 項目の処理済みと未処理でスタイルを変える
- React + Redux入門 04: リスト項目のフィルタを加える
- React + Redux入門 05: PropTypesで型を確かめる
- React + Redux入門 06: コンポーネントをプレゼンテーションとコンテナに分ける
- React + Redux入門 07: React ReduxのフックuseDispatch()とuseSelector()を使う
- React + Redux入門 08: Redux公式サイトのTodoリストの作例をフックuseDispatch()とuseSelector()で書き替える
作成者: 野中文雄
作成日: 2019年8月9日
Copyright © 2001-2019 Fumio Nonaka. All rights reserved.