HTML5テクニカルノート
React + Redux入門 04: リスト項目のフィルタを加える
- ID: FN1908004
- Technique: HTML5 / ECMAScript 2015
- Library: React 16.8.6
「React + Redux入門 03: 項目の処理済みと未処理でスタイルを変える」では、リストの各項目にチェックサインをつけて、未処理の項目はテキストのスタイルが変わるようにしました。さらに、項目のリスト表示を、すべてのほか処理済みまたは未処理だけに切り替えられるフィルタの機能を加えましょう。
01 フィルタのAction Creatorを加える
まずは、モジュールsrc/actions/index.js
にAction CreatorのsetVisibilityFilter
を加えます。併せてオブジェクトVisibilityFilters
に定めたのは、Actionオブジェクトのプロパティfilter
がとる定数です。
src/actions/index.jsexport const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER'; export const setVisibilityFilter = (filter) => ({ type: SET_VISIBILITY_FILTER, filter }); export const VisibilityFilters = { SHOW_ALL: 'SHOW_ALL', SHOW_COMPLETED: 'SHOW_COMPLETED', SHOW_ACTIVE: 'SHOW_ACTIVE' };
02 3つのフィルタボタンが備わったコンポーネントをつくる
新たにつくるモジュールsrc/components/Footer.js
は、3つのフィルタボタンを備えるフッタのコンポーネントです(コード001)。ボタンは子コンポーネント(Link
)としてこのあと定め、それぞれのフィルタ定数を属性filter
に与えます。
コード001■src/components/Footer.js
import React from 'react';
import Link from './Link';
import { VisibilityFilters } from '../actions';
const Footer = () => (
<div>
<span>Show: </span>
<Link filter={VisibilityFilters.SHOW_ALL}>
All
</Link>
<Link filter={VisibilityFilters.SHOW_ACTIVE}>
Active
</Link>
<Link filter={VisibilityFilters.SHOW_COMPLETED}>
Completed
</Link>
</div>
);
export default Footer;
フィルタボタンとして新規に加えるモジュールsrc/components/Link.js
の記述は、以下のコード002のとおりです。connect()
関数の第1および第2引数に渡す関数(mapStateToProps()
とmapDispatchToProps()
)は、いずれも第2引数(ownProps
)としてラッパーコンポーネントに与えられたプロパティの参照を受け取ります。このプロパティから取り出すのは、前掲コード001で親コンポーネントから与えられたfilter
属性の値(フィルタ定数)です。
ボタンがクリックされると、フィルタ定数を納めたActionオブジェクトがStoreに送られます。そして、定数がフィルタボタンと一致するとdisabled
属性がtrue
になって、スタイルが変わる仕組みです。
コード002■src/components/Link.js
import React from 'react';
import { connect } from 'react-redux';
import { setVisibilityFilter } from '../actions';
const Link = ({ active, children, onClick }) => (
<button
onClick={onClick}
disabled={active}
style={{
marginLeft: '4px',
}}
>
{children}
</button>
);
const mapStateToProps = (state, ownProps) => ({
active: ownProps.filter === state.visibilityFilter
});
const mapDispatchToProps = (dispatch, ownProps) => ({
onClick: () => dispatch(setVisibilityFilter(ownProps.filter))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Link);
まだReducerがつくられていないので、スタイルは変わりません。それでも、アプリケーションのモジュールsrc/components/App.js
にフッタ(Footer
)を組み込めば、3つのフィルタボタンが表れるでしょう(図001)。
src/components/App.jsimport Footer from './Footer'; const App = () => ( <div> <Footer /> </div> );
図001■3つのフィルタボタンが加わる
03 フィルタ切り替えのReducerをつくる
新しいモジュールsrc/reducers/visibilityFilter.js
として、フィルタ切り替えのReducerをつぎのコード003のように定めましょう。送られてきたActionから新たに選ばれたフィルタ定数(filter
)を取り出して返します。
コード003■src/reducers/visibilityFilter.js
import { VisibilityFilters } from '../actions';
const visibilityFilter = (state = VisibilityFilters.SHOW_ALL, action) => {
switch (action.type) {
case 'SET_VISIBILITY_FILTER':
return action.filter;
default:
return state;
}
};
export default visibilityFilter;
そして、新しいReducerをsrc/reducers/index.js
のcombineReducers()
メソッドの引数オブジェクトに加えましょう。これで、選んだフィルタボタンのスタイルが変わるはずです(図002)。
src/reducers/index.jsimport visibilityFilter from './visibilityFilter'; export default combineReducers({ visibilityFilter });
図002■選ばれたフィルタボタンのスタイルが変わる
04 表示するリスト項目をフィルタで切り替える
項目をフィルタリングする関数(getVisibleTodos
)を加えるのは、モジュールsrc/components/TodoList.js
です。mapStateToProps()
から呼び出して、選択されたフィルタに応じた項目を配列にしてコンポーネントのtodos
プロパティに与えます。
src/components/TodoList.js// import { toggleTodo } from '../actions'; import { toggleTodo, VisibilityFilters } from '../actions'; const getVisibleTodos = (todos, filter) => { switch (filter) { case VisibilityFilters.SHOW_ALL: return todos case VisibilityFilters.SHOW_COMPLETED: return todos.filter(t => t.completed) case VisibilityFilters.SHOW_ACTIVE: return todos.filter(t => !t.completed) default: throw new Error('Unknown filter: ' + filter) } } const mapStateToProps = (state) => ({ // todos: state.todos todos: getVisibleTodos(state.todos, state.visibilityFilter) });
これで、フィルタボタンにより未処理や表示済みなど、項目のリストが切り替えられるでしょう(図003)。これまで書き替えた各モジュールのコード全体は、以下のコード004から007に掲げたとおりです。すべてのファイルのコードと実際の動きは、CodePenに上げたサンプル001でお確かめください。
図003■処理済みと未処理の項目表示をフィルタで切り替える
コード004■src/actions/index.js
export const ADD_TODO = 'ADD_TODO';
export const TOGGLE_TODO = 'TOGGLE_TODO';
export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER';
let nextTodoId = 0;
export const addTodo = (text) => ({
type: ADD_TODO,
id: nextTodoId++,
text
});
export const toggleTodo = (id) => ({
type: TOGGLE_TODO,
id
});
export const setVisibilityFilter = (filter) => ({
type: SET_VISIBILITY_FILTER,
filter
});
export const VisibilityFilters = {
SHOW_ALL: 'SHOW_ALL',
SHOW_COMPLETED: 'SHOW_COMPLETED',
SHOW_ACTIVE: 'SHOW_ACTIVE'
};
コード005■src/components/App.js
import React from 'react';
import AddTodo from './AddTodo';
import TodoList from './TodoList';
import Footer from './Footer';
import '../App.css';
const App = () => (
<div>
<AddTodo />
<TodoList />
<Footer />
</div>
);
export default App;
コード006■src/reducers/index.js
import { combineReducers } from 'redux';
import todos from './todos';
import visibilityFilter from './visibilityFilter';
export default combineReducers({
todos,
visibilityFilter
});
コード007■src/components/TodoList.js
import React from 'react';
import { connect } from 'react-redux';
import { toggleTodo, VisibilityFilters } 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 getVisibleTodos = (todos, filter) => {
switch (filter) {
case VisibilityFilters.SHOW_ALL:
return todos
case VisibilityFilters.SHOW_COMPLETED:
return todos.filter(t => t.completed)
case VisibilityFilters.SHOW_ACTIVE:
return todos.filter(t => !t.completed)
default:
throw new Error('Unknown filter: ' + filter)
}
}
const mapStateToProps = (state) => ({
todos: getVisibleTodos(state.todos, state.visibilityFilter)
});
const mapDispatchToProps = (dispatch) => ({
toggleTodo: (id) => dispatch(toggleTodo(id))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(TodoList);
サンプル001■react-redux-todos-04
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月15日
Copyright © 2001-2019 Fumio Nonaka. All rights reserved.