HTML5テクニカルノート
React入門 05: フォームからサーバーにデータを送ってページに加える
- ID: FN1608006
- Technique: HTML5 / JavaScript
- Library: React 15.4.2
フォームに入力したデータをサーバーに送ったうえで、ページに加えます。「React入門 04: JSONデータをローカルサーバーから読み込んでページに表示する」で、AJAXで読み込んだデータのテキストをページの要素に差し込むことはできました。そこで書き上げたコード001「ローカルサーバーから読み込んだJSONデータをHTMLページに差し込む」に、さらに手を加えてゆきます。
01 送信ボタンでフォームのデータを得る
フォームのコンポーネント(CommentForm)に、つぎのようにテキスト入力フィールドふたつと送信ボタンを加えます(図001)。
var CommentForm = React.createClass({ render: function() { return ( <form className="commentForm"> <input type="text" placeholder="名前" /> <input type="text" placeholder="コメントを入力" /> <input type="submit" value="送信" /> </form> ); } });
図001■フォームにテキスト入力フィールドと送信ボタンが加わった
送信するデータの扱いはReactで行いますので、以下のように<form>
要素には、onSubmit
属性でコンポーネント(CommentForm)のメソッド(handleSubmit())の呼び出しを定めました。そして、メソッド本体の初めに、送信のデフォルトの動作をevent.preventDefault()
メソッドで止めます。
Reactのコンポーネントのやり取りで変更するデータは、state
プロパティに定めなければなりません。そこで、フォームのコンポーネント(CommentForm)に、getInitialState()
メソッドでデータの初期値を定めました。すると、onSubmit
ハンドラのメソッド(handleSubmit())で、そのデータが取り出せます。その後データをサーバーに送る処理の代わりとして、console.log()
メソッドでstate
プロパティに与えられたデータを確認しています。
var CommentForm = React.createClass({ getInitialState: function() { return { author: '湯川 秀樹', text: 'アイデアの秘訣は執念である。' }; }, handleSubmit: function(eventObject) { eventObject.preventDefault(); var author = this.state.author; var text = this.state.text; console.log(author, text); // 確認用 }, render: function() { return ( <form className="commentForm" onSubmit={this.handleSubmit}> </form> ); } });
これで、フォームの送信ボタンを押すと、getInitialState()
メソッドでstate
プロパティに定めたデータの初期値がコンソールに示されます(図002)。ただし、テキスト入力フィールドにはデータがテキストとして表れませんし、入力してもstate
プロパティのデータは変わりません。
図002■送信ボタンを押すとデータの初期値がコンソールに示される
02 フォームのデータをテキスト入力フィールドに表示する
まず、state
プロパティに定めたデータの初期値を、テキスト入力フィールドに与えます。そのためには、state
プロパティのデータを、つぎのように<input>
要素のvalue
属性に定めればよいのです。これで、初期値のデータのテキストが、テキスト入力フィールドに示されます(図003)。その代わり、state
プロパティのデータを変えないかぎり、テキスト入力フィールドのテキストはそのまま変わらないことになります。
var CommentForm = React.createClass({ render: function() { return ( <form className="commentForm" onSubmit={this.handleSubmit}> <input type="text" placeholder="名前" value={this.state.author} /> <input type="text" placeholder="コメントを入力" value={this.state.text} /> <input type="submit" value="送信" /> </form> ); } });
図003■初期値のデータのテキストがテキスト入力フィールドに示される
03 テキスト入力フィールドでデータを編集する
そこで、入力したテキストをstate
プロパティのデータに反映させます。そのとき使うのがonChange
イベントハンドラです。Reactは基本的に標準のDOMの仕様にしたがっています。けれど、onChange
イベントハンドラは少し違っています。標準のonChange
イベントは、テキスト入力フィールドからフォーカスを外したときに生じます(「JavaScript:テキスト入力関連のイベント、onChange,onInput,onKeyUp」参照)。ReacrのonChange
イベントは値が変わるたびに起こりますので、標準のonInput
イベントに準じます。
つぎのように<input>
要素にonChange
属性を定め、イベントハンドラの関数本体から呼び出すsetState()
メソッドに編集後の値を渡せば、テキスト入力フィールドの文字は入力に応じて変わり、送信ボタンで新たなデータがコンソールに示されます(図004)。
var CommentForm = React.createClass({ handleAuthorChange: function(eventObject) { this.setState({author: eventObject.target.value}); }, handleTextChange: function(eventObject) { this.setState({text: eventObject.target.value}); }, render: function() { return ( <form className="commentForm" onSubmit={this.handleSubmit}> <input value={this.state.author} onChange={this.handleAuthorChange} /> <input value={this.state.text} onChange={this.handleTextChange} /> <input type="submit" value="送信" /> </form> ); } });
図004■テキスト入力フィールドで編集して送信ボタンを押すと編集後のデータが示される
テキスト入力フィールドを使ってstate
プロパティのデータが変えられるようになりましたので、つぎのようにgetInitialState()
メソッドの初期値は空にしましょう。そして、送信のメソッド(handleSubmit())も新たなstate
プロパティのデータを確かめたら、テキスト入力フィールドは空欄に戻します。ここまでの書き替えを、以下のコード001にまとめました。
var CommentForm = React.createClass({ getInitialState: function() { return {author: '', text: ''}; }, handleSubmit: function(eventObject) { eventObject.preventDefault(); var author = this.state.author; var text = this.state.text; console.log(author, text); // 確認用 this.setState({author: '', text: ''}); }, });
コード001■フォームに入力したデータをstateプロパティに定める
var CommentBox = React.createClass({
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, error) {
console.error(this.props.url, status, error.toString());
}.bind(this)
});
},
render: function() {
return (
<div className="commentBox">
<h1>コメント</h1>
<CommentList data={this.state.data} />
<CommentForm />
</div>
);
}
});
var CommentList = React.createClass({
render: function() {
var commentNodes = this.props.data.map(function(comment) {
return (
<Comment author={comment.author} key={comment.id}>
{comment.text}
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var Comment = React.createClass({
rawMarkup: function() {
var markDown = new Remarkable();
var rawMarkup = markDown.render(this.props.children.toString());
return { __html: rawMarkup };
},
render: function() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={this.rawMarkup()} />
</div>
);
}
});
var CommentForm = React.createClass({
getInitialState: function() {
return {author: '', text: ''};
},
handleAuthorChange: function(eventObject) {
this.setState({author: eventObject.target.value});
},
handleTextChange: function(eventObject) {
this.setState({text: eventObject.target.value});
},
handleSubmit: function(eventObject) {
eventObject.preventDefault();
var author = this.state.author;
var text = this.state.text;
console.log(author, text); // 確認用
this.setState({author: '', text: ''});
},
render: function() {
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<input
type="text"
placeholder="名前"
value={this.state.author}
onChange={this.handleAuthorChange}
/>
<input
type="text"
placeholder="コメントを入力"
value={this.state.text}
onChange={this.handleTextChange}
/>
<input type="submit" value="送信" />
</form>
);
}
});
ReactDOM.render(
<CommentBox url="/api/comments" />,
document.getElementById('content')
);
04 フォームに入力されたデータをサーバーに送る
いよいよ、フォームに入力されたデータを、送信ボタンでサーバーに送る準備にかかります。state
プロパティをもつのは親コンポーネント(CommentBox)です。したがって、送信のメソッド(handleCommentSubmit())はつぎのように親コンポーネントに定め、フォームのコンポーネント(CommentForm)の送信ボタンが押されたとき新たなデータを引数にして呼び出すことにします。
var CommentBox = React.createClass({ handleCommentSubmit: function(comment) { console.log(comment); // 確認用 }, render: function() { return ( <div className="commentBox"> <CommentForm onCommentSubmit={this.handleCommentSubmit} /> </div> ); } }); var CommentForm = React.createClass({ handleSubmit: function(eventObject) { var author = this.state.author; var text = this.state.text; // console.log(author, text); this.props.onCommentSubmit({author: author, text: text}); }, render: function() { return ( <form className="commentForm" onSubmit={this.handleSubmit}> </form> ); } });
送信のメソッド(handleCommentSubmit())は、つぎのようにjQuery.ajax()
メソッドでデータをサーバーに送ります。引数のオブジェクトに定めたプロパティは、初期設定のメソッド(componentDidMount())で用いたものも含めて、以下の表001にまとめました(「$.ajax()」参照)。これで、フォームに入力したデータが送信ボタンでサーバーに送られ、ページに加えられるようになります(図005)。
var CommentBox = React.createClass({ handleCommentSubmit: function(comment) { $.ajax({ url: this.props.url, dataType: 'json', type: 'POST', data: comment, success: function(data) { this.setState({data: data}); }.bind(this), error: function(xhr, status, error) { console.error(this.props.url, status, error.toString()); }.bind(this) }); }, render: function() { return ( <div className="commentBox"> <CommentForm onCommentSubmit={this.handleCommentSubmit} /> </div> ); } });
図005■フォームに入力したデータが送信ボタンでサーバーに送られてページに加わる
表001■jQuery.ajax()メソッドに渡した引数のオブジェクトに定めたプロパティ
jQuery.ajax()メソッドの引数に与えられるプロパティ | |
url | リクエストの送り先URLを示す文字列。 |
dataType |
サーバーから返されるデータのタイプを示す文字列。つぎの種類がある。
|
cache | false を与えると、リクエストされたページをブラウザにキャッシュさせない。デフォルト値はtrue 。 |
type | データを受け渡すメソッドで、GET(デフォルト値)またはPOST。 |
data | サーバーに送るデータとなるオブジェクトまたは文字列あるいは配列。オブジェクトにはキー(プロパティ)とその値を納める。 |
success | リクエストが成功したときに呼び出す関数。第1引数にはdataType にもとづいてフォーマットされたデータが渡される。 |
error | リクエストが失敗したときに呼び出す関数。第1引数にXMLHttpRequest(jqXHR)オブジェクト、第2引数にはエラーの種類を示す文字列、第3引数にHTTPステータスのテキスト部分が渡される。 |
05 処理を仕上げる
前項までの書き替えで、基本の動きはできました。けれど、細かく見るとまだ至らないところがあります。テキスト入力フィールドが空欄であっても、つぎのJSONファイルが示すように空のままデータを送ってしまうのです。
[ { "id": 1388534400000, "author": "ヘンリー・キッシンジャー", "text": "チャンスは__貯金__できない。" }, { "id": 1420070400000, "author": "マーク・トウェイン", "text": "禁煙なんてたやすい。私は*何千回*もやった。" }, { "id": 1471595483183, "author": "湯川 秀樹", "text": "アイデアの秘訣は執念である。" }, { "id": 1471596010480, "author": "", "text": "" } ]
そこで、つぎのようにテキストの入力が空の場合にはデータを送らないようにします。さらに、空白スペースだけで通ってしまわないように、データのテキストに対してString.trim()
メソッドを呼び出しました。
var CommentForm = React.createClass({ handleSubmit: function(eventObject) { var author = this.state.author.trim(); var text = this.state.text.trim(); if (!author || !text) { return; } this.props.onCommentSubmit({author: author, text: text}); }, }
もうひとつ、テキスト入力フィールドの<input>
要素のonChange
属性には、フィールドごとに異なるイベントハンドラのメソッドを与えました。入力欄によって処理が違うならそうすべきです。けれども、この作例でやっているのは、入力をstate
プロパティのデータに反映させているだけです。そうであれば、同じメソッドをハンドラに定めた方がすっきりします。そこで、つぎのように<input>
要素にvalue
属性の値と同じid
属性を加えます。そして、onChange
ハンドラには同じメソッド(handleChange())を新たに与えて、メソッドはid
属性値により、設定するstate
プロパティのデータを決めるようにしました。
var CommentForm = React.createClass({ /* handleAuthorChange: function(eventObject) { this.setState({author: eventObject.target.value}); }, handleTextChange: function(eventObject) { this.setState({text: eventObject.target.value}); }, */ handleChange: function(eventObject) { var state = {}; state[eventObject.target.id] = eventObject.target.value; this.setState(state); }, render: function() { return ( <form className="commentForm" onSubmit={this.handleSubmit}> <input id="author" type="text" value={this.state.author} onChange={this.handleChange} /> <input id="text" type="text" value={this.state.text} onChange={this.handleChange} /> </form> ); } });
これらの手直しも加えたのが、つぎのコード002です。フォームのテキスト入力フィールドに書き加えたデータをサーバーに送り、新たなデータがページに加わります。未記入あるいは空白スペースだけの入力欄があれば、データは送られません。
コード002■フォームに入力したデータをサーバーに送ってページに加える
var CommentBox = React.createClass({
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, error) {
console.error(this.props.url, status, error.toString());
}.bind(this)
});
},
handleCommentSubmit: function(comment) {
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: comment,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, error) {
console.error(this.props.url, status, error.toString());
}.bind(this)
});
},
render: function() {
return (
<div className="commentBox">
<h1>コメント</h1>
<CommentList data={this.state.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
});
var CommentList = React.createClass({
render: function() {
var commentNodes = this.props.data.map(function(comment) {
return (
<Comment author={comment.author} key={comment.id}>
{comment.text}
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var Comment = React.createClass({
rawMarkup: function() {
var markDown = new Remarkable();
var rawMarkup = markDown.render(this.props.children.toString());
return { __html: rawMarkup };
},
render: function() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={this.rawMarkup()} />
</div>
);
}
});
var CommentForm = React.createClass({
getInitialState: function() {
return {author: '', text: ''};
},
handleChange: function(eventObject) {
var state = {};
state[eventObject.target.id] = eventObject.target.value;
this.setState(state);
},
handleSubmit: function(eventObject) {
eventObject.preventDefault();
var author = this.state.author.trim();
var text = this.state.text.trim();
if (!author || !text) {
return;
}
this.props.onCommentSubmit({author: author, text: text});
this.setState({author: '', text: ''});
},
render: function() {
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<input
type="text"
id="author"
placeholder="名前"
value={this.state.author}
onChange={this.handleChange}
/>
<input
type="text"
id="text"
placeholder="コメントを入力"
value={this.state.text}
onChange={this.handleChange}
/>
<input type="submit" value="送信" />
</form>
);
}
});
ReactDOM.render(
<CommentBox url="/api/comments" />,
document.getElementById('content')
);
- React: まずは動かしてみる
- React入門 01: コンポーネントを組み立てる
- React入門 02: remarkableでMarkdownの機能を加える
- React入門 03: データはJSON形式にしてページをローカルサーバーで表示する
- React入門 04: JSONデータをローカルサーバーから読み込んでページに表示する
- React入門 06: ローカルサーバーの立ち上げとJSXのコンパイル
作成者: 野中文雄
更新日: 2017年3月13日 ライブラリのバージョン更新にともなう修正。
更新日: 2016年8月30日 末尾のリンクにReact入門 06を追加
更新日: 2016年8月23日 スクリプトを一部修正。
更新日: 2016年8月21日 末尾にReact入門シリーズのリンクを追加。
作成日: 2016年8月20日
Copyright © 2001-2017 Fumio Nonaka. All rights reserved.