サイトトップ

Director Flash 書籍 業務内容 プロフィール

HTML5テクニカルノート

React入門 05: フォームからサーバーにデータを送ってページに加える


フォームに入力したデータをサーバーに送ったうえで、ページに加えます。「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■フォームにテキスト入力フィールドと送信ボタンが加わった

図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■送信ボタンを押すとデータの初期値がコンソールに示される

図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■初期値のデータのテキストがテキスト入力フィールドに示される

図003

03 テキスト入力フィールドでデータを編集する

そこで、入力したテキストをstateプロパティのデータに反映させます。そのとき使うのがonChangeイベントハンドラです。Reactは基本的に標準のDOMの仕様にしたがっています。けれど、onChangeイベントハンドラは少し違っています(「DOM Differences」参照)。標準の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■テキスト入力フィールドで編集して送信ボタンを押すと編集後のデータが示される

図004上
図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■フォームに入力したデータが送信ボタンでサーバーに送られてページに加わる

図005

表001■jQuery.ajax()メソッドに渡した引数のオブジェクトに定めたプロパティ

jQuery.ajax()メソッドの引数に与えられるプロパティ
url リクエストの送り先URLを示す文字列。
dataType サーバーから返されるデータのタイプを示す文字列。つぎの種類がある。
  • xml
  • html
  • script
  • json
  • jsonp
  • text
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')
);


作成者: 野中文雄
更新日: 2016年8月30日 末尾のリンクにReact入門 06を追加
更新日: 2016年8月23日 スクリプトを一部修正。
更新日: 2016年8月21日 末尾にReact入門シリーズのリンクを追加。
作成日: 2016年8月20日


Copyright © 2001-2016 Fumio Nonaka.  All rights reserved.