HTML5テクニカルノート
React + Redux入門 05: PropTypesで型を確かめる
- ID: FN1908005
- Technique: HTML5 / ECMAScript 2015
- Library: React 16.8.6
Reactに備わる型チェックの機能がPropTypes
です(「PropTypesを用いた型チェック」参照)。コンポーネントに与えられるプロパティ(props
)のデータを、さまざまなかたちで確かめられます。「React + Redux入門 04: リスト項目のフィルタを加える」で書いたコードにPropTypes
の型チェックを加えましょう。なお、パフォーマンスの理由により、チェックが働くのは開発のときだけです。
01 PropTypesをどう使うか
モジュールsrc/components/AddTodo.js
の関数コンポーネントAddTodo
は、connect()
関数により与えられたdispatch()
を引数オブジェクトのプロパティとして受け取ります。PropTypes
を使えば、この型が確かめられるのです。
つぎのようにprop-types
からPropTypes
をインポートすると、コンポーネントにpropTypes
が割り当てられます。与えるオブジェクトに加えるのが、チェックするプロパティ(dispatch
)です。PropTypes
のプロパティで型を決めます。関数(function
)はPropTypes.func
です。そのままでは、省くことのできる(オプション)扱いになります。必須であることを示すのが、あとに添えたisRequired
です。
src/components/AddTodo.jsimport PropTypes from 'prop-types'; const AddTodo = ({ dispatch }) => { }; AddTodo.propTypes = { // dispatch: PropTypes.string // 型が合わないので警告が示される dispatch: PropTypes.func.isRequired }; export default connect()(AddTodo);
コメントアウトしたコードのように合わない型を定めると、つぎのような警告が示されます。なお、PropTypes.string
は文字列の型です。
Warning: Failed prop type: Invalid propdispatch
of typefunction
supplied toAddTodo
, expectedstring
.
モジュールsrc/components/Link.js
とsrc/components/Todo.js
も、同じようにプロパティのデータ型が定められます。PropTypes
のバリデータプロパティは、つぎの表001のとおりです。前掲src/components/AddTodo.js
と合わせて、以下のコード001から003にそれぞれモジュールの記述全体を掲げます。
表001■PropTypesのバリデータプロパティ
プロパティ | データ型 |
---|---|
PropTypes.array | 配列 |
PropTypes.bool | ブール(論理)値 |
PropTypes.func | 関数 |
PropTypes.number | 数値 |
PropTypes.object | オブジェクト |
PropTypes.string | 文字列 |
PropTypes.symbol | symbol |
PropTypes.node | ノード |
コード001■src/components/AddTodo.js
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { addTodo } from '../actions';
const AddTodo = ({ dispatch }) => {
let input;
return (
<div>
<form onSubmit={(event) => {
event.preventDefault();
const text = input.value.trim();
input.value = '';
if (!text) {
return;
}
dispatch(addTodo(text));
}}>
<input ref={(element) => input = element} />
<button type="submit">
Add Todo
</button>
</form>
</div>
);
};
AddTodo.propTypes = {
dispatch: PropTypes.func.isRequired
};
export default connect()(AddTodo);
コード002■src/components/Link.js
import React from 'react';
import PropTypes from 'prop-types';
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))
});
Link.propTypes = {
active: PropTypes.bool.isRequired,
children: PropTypes.node.isRequired,
onClick: PropTypes.func.isRequired
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(Link);
コード003■src/components/Todo.js
import React from 'react';
import PropTypes from 'prop-types';
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>
);
Todo.propTypes = {
onClick: PropTypes.func.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
};
export default Todo;
02 オブジェクトのプロパティに型を定める
プロパティがオブジェクトの場合について考えましょう。そのため、試しにモジュールsrc/components/TodoList.js
とsrc/components/Todo.js
をつぎのように書き替えたとします。
src/components/TodoList.jssrc/components/Todo.jsconst TodoList = ({ todos, toggleTodo }) => ( <ul className="todo-list"> {todos.map((todo) => <Todo // {...todo} todo={todo} /> )} </ul> );
// const Todo = ({ onClick, completed, text }) => ( const Todo = ({ onClick, todo }) => { const completed = todo.completed; const text = todo.text; return ( ); };
このとき、関数コンポーネントTodo
が受け取るオブジェクトのプロパティtodo
のデータ型をどのように定めたらよいかです。PropTypes.object
は使えます。けれども、それではプロパティがオブジェクトであることしか決まりません。どういうプロパティにどのような値が納められているかわからないのです。
src/components/Todo.jsTodo.propTypes = { // completed: PropTypes.bool.isRequired, // text: PropTypes.string.isRequired todo: PropTypes.object.isRequired };
このようなときには、関数PropTypes.shape()
を用いてください。引数のオブジェクトにプロパティとPropTypes
のバリデータがつぎのように書き表せるのです。オブジェクトtodo
にはほかにもプロパティ(id
)があります。けれど、そのデータ型をチェックしなくてよいなら省いても構いません。
src/components/Todo.jsTodo.propTypes = { // todo: PropTypes.object.isRequired todo: PropTypes.shape({ completed: PropTypes.bool.isRequired, text: PropTypes.string.isRequired }).isRequired };
モジュールsrc/components/TodoList.js
とsrc/components/Todo.js
は、もとに戻します。
03 オブジェクトの配列に型を定める
モジュールsrc/components/TodoList.js
の関数コンポーネントは、オブジェクトの配列(todos
)と関数(toggleTodo
)をプロパティとして受け取ります。この配列をどのように型づけけるかです。
src/components/TodoList.jsconst TodoList = ({ todos, toggleTodo }) => ( <ul className="todo-list"> {todos.map((todo) => )} </ul> );
オブジェクトの場合と同じく、PropTypes.array
では配列であることしか決められません。それに対して、PropTypes.arrayOf()
を用いれば、引数にバリデータが与えられます。さらに、PropTypes.shape()
も引数にできるのです。つぎのように、要素のオブジェクトに型が定められます。
src/components/TodoList.jsimport PropTypes from 'prop-types'; const TodoList = ({ todos, toggleTodo }) => ( ); TodoList.propTypes = { todos: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.number.isRequired, completed: PropTypes.bool.isRequired, text: PropTypes.string.isRequired }).isRequired).isRequired, };
書き改めたモジュールsrc/components/TodoList.js
の記述全体は、つぎのコード004のとおりです。サンプル001をCodePenに掲げました。
コード004■src/components/TodoList.js
import React from 'react';
import PropTypes from 'prop-types';
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))
});
TodoList.propTypes = {
todos: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
}).isRequired).isRequired,
toggleTodo: PropTypes.func.isRequired
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(TodoList);
サンプル001■react-redux-todos-05
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月21日
Copyright © 2001-2019 Fumio Nonaka. All rights reserved.