#概要
前回の記事までは、Todoを追加する『Add Todo』と、Todoの未・済を切り替える『Toggle Todo』の機能を実装して参りました。今回は、選択したフィルタによって表示するTodoを変更する__『Filter Todo』__の機能を実装していきたいと思います!
『Add Todo』に関してはこちら
『Toggle Todo』に関してはこちら
#完成品
『All』,『Active』,『Completed』のリンクを押すことで、ページに表示されるTodoが変わります!
#Action Creatorの作成
新たにsetVisibilityFilter
を追加します。ここでは、フィルター(SHOW_COMPLETEDなど)を受け取り、typeとfilterを返します。
export const ADD_TODO = 'ADD_TODO';
export const TOGGLE_TODO = 'TOGGLE_TODO';
export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER';
export const VisibilityFilters = {
SHOW_ALL: 'SHOW_ALL',
SHOW_COMPLETED: 'SHOW_COMPLETED',
SHOW_ACTIVE: 'SHOW_ACTIVE',
};
let nextTodoId = 0;
export const addTodo = text => {
return {
type: ADD_TODO,
id: nextTodoId++,
text,
//text: text,
};
};
export const toggleTodo = id => {
return {
type: TOGGLE_TODO,
id,
//index: index
};
};
export const setVisibilityFilter = filter => {
return {
type: SET_VISIBILITY_FILTER,
filter,
// filter: filter
};
};
#reducerの作成
初期stateをSHOW_ALL
とし、action.type
がSET_VISIBILITY_FILTER
の際にaction.filter
を新しいstateとして返します。
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関数に加えましょう!
import {combineReducers} from 'redux';
import todos from './todos';
import visibilityFilter from './visibilityFilter';
const todoApp = combineReducers({todos, visibilityFilter});
export default todoApp;
#動作確認
src/index.js
にて、正しくデータが格納されるか手動で確認してみましょう!
import {addTodo, toggleTodo, setVisibilityFilter} from './actions';
console.log(store.getState()); /// "SHOW_ALL"
store.dispatch(setVisibilityFilter('SHOW_COMPLETED'));
console.log(store.getState()); /// "SHOW_COMPLETED"
ここまでで、Action Creatorとreducerを作成し、フィルターの値をstoreに格納することができました。
次からは、フィルターの値によってviewの表示を変更できるようにしましょう!
#VisibleTodoListを修正する
todos.filter()のように配列のメソッドのフィルタを用いることで、todoのcompleted属性によって、新たに作成した配列を返します。
import {connect} from 'react-redux';
import TodoList from '../components/TodoList';
import {toggleTodo, VisibilityFilters} from '../actions';
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case VisibilityFilters.SHOW_ALL:
return todos;
case VisibilityFilters.SHOW_COMPLETED:
return todos.filter(todo => todo.completed); //todos.filter()は配列のメソッドのフィルタ
case VisibilityFilters.SHOW_ACTIVE:
return todos.filter(todo => !todo.completed); //todos.filter()は配列のメソッドのフィルタ
}
};
const mapStateToPorops = state => {
return {todos: getVisibleTodos(state.todos, state.visibilityFilter)};
};
const mapDispatchToProps = dispatch => {
return {
toggleTodo: id => {
dispatch(toggleTodo(id));
},
};
};
const VisibleTodoList = connect(
mapStateToPorops,
mapDispatchToProps
)(TodoList);
export default VisibleTodoList;
先ほどと同様にsrc/index.js
にて手動で動作確認をしましょう!
import {addTodo, toggleTodo, setVisibilityFilter} from './actions';
store.dispatch(setVisibilityFilter('SHOW_COMPLETED'));
#Linkを作成する
表示したいTodoの種類をLinkをクリックすることによって表示できるようにします。
まずは、とりあえずLinkを表示させましょう!
props.childrenはコンポーネントの中身を取得できます。Linkコンポーネントを使うときの、<Link>xxx</Link>
のxxx
です。
import React from 'react';
import PropTypes from 'prop-types';
const Link = ({children, onClick}) => {
return (
<a href="#">{children}</a>
);
};
Link.propTypes = {
children: PropTypes.node.isRequired,
};
export default Link;
LinkコンポーネントはFooterコンポーネントで使用します。
import React from 'react';
import FilterLink from '../containers/FilterLink';
const Footer = () => {
return (
<p>
Show: <Link>All</Link>
{', '}
<Link>Active</Link>
{', '}
<Link>Completed</Link>
</p>
);
};
export default Footer;
FooterコンポーネントはAppコンポーネントで表示します!
import React from 'react';
import VisibleTodoList from '../containers/VisibleTodoList';
import AddTodo from '../containers/AddTodo';
import Footer from './Footer';
const App = () => {
return (
<div className="App">
<AddTodo />
<VisibleTodoList />
<Footer />
</div>
);
};
export default App;
これでリンクの表示が完了です。
#リンクをクリックしたときにフィルターの値を変える
リンクをクリックした際にフィルターの値(SHOW_ALLなど)を変えるには、リンクをクリックした際に、dispatch(setVisibilityFilter())
を呼び出せるようにします。
connect関数を使って、propsにdispatchを渡せるようにしましょう!
import {connect} from 'react-redux';
import {setVisibilityFilter} from '../actions';
import Link from '../components/Link';
const mapStateToProps = (state, ownProps) => {
return {state: state};
};
const mapDispatchToProps = (dispatch, ownProps) => {
return {
onClick: () => {
dispatch(setVisibilityFilter(ownProps.filter));
},
};
};
const FilterLink = connect(
mapStateToProps,
mapDispatchToProps
)(Link);
export default FilterLink;
src/components/Footer.js
でLinkとmapStateToProps,mapDispatchToProps
をconnectさせた『FilterLink』を使います。
この際に、mapDispatchToPropsのonClick関数にフィルターの値を渡すためfilter="SHOW_ALL"
のように記述します。
import React from 'react';
import FilterLink from '../containers/FilterLink';
const Footer = () => {
return (
<p>
Show: <FilterLink filter="SHOW_ALL">All</FilterLink>
{', '}
<FilterLink filter="SHOW_ACTIVE">Active</FilterLink>
{', '}
<FilterLink filter="SHOW_COMPLETED">Completed</FilterLink>
</p>
);
};
export default Footer;
#Linkをクリックした際にonClick関数を呼ぶ
『preventDefault』は『デフォルトの動作を発生させない』という意味です。今回はaタグの中で使っているのですが、これは『aタグのherfで指定されたURLへ遷移する動作を発生させない』という意味を持っております。
import React from 'react';
import PropTypes from 'prop-types';
const Link = ({children, onClick}) => {
return (
<a
href="#"
onClick={e => {
e.preventDefault();
onClick();
}}>
{children}
</a>
);
};
Link.propTypes = {
children: PropTypes.node.isRequired,
onClick: PropTypes.func.isRequired,
};
export default Link;
これで、Linkを押すとviewの表示が切り替わる動作を実装することができました。
(Linkクリック→onClick関数
が呼び出される→dispatch(setVisibilityFilter())
が呼び出される→storeに保存されているフィルターの値が更新→viewが書き換わる)
#現在activeなリンクを押せなくする
現在activeなリンクを押せなくするために、activeなリンクをただのテキストに変更する機能を実装いたします。
ここでは,Linkコンポーネントの現在の状態を知るためにprops.active
としてデータを渡します。
const mapStateToProps = (state, ownProps) => {
return {active: ownProps.filter === state.visibilityFilter};
};
そして、active
の状態によってテキストを返すか、リンクを返すかをsrc/components/Link.js
にて判断いたします。
import React from 'react';
import PropTypes from 'prop-types';
const Link = ({active, children, onClick}) => {
if (active) {
return <span>{children}</span>;
}
return (
<a
href="#"
onClick={e => {
e.preventDefault();
onClick();
}}>
{children}
</a>
);
};
Link.propTypes = {
children: PropTypes.node.isRequired,
onClick: PropTypes.func.isRequired,
};
export default Link;
以上で、『Filter Todo』の実装は完了です!
#公式のBasicTutorial完了
これで公式のBasicTutorialと同じ機能が実装できたように思います。
#リファレンス