いまさら聞けないReact、Virtual DOM、JSX超入門(3/3 ページ)

» 2016年07月28日 05時00分 公開
[諏訪悠紀クラスメソッド]
前のページへ 1|2|3       

Reactのコンポーネントを組み合わせてシンプルなWebアプリを作ってみよう

 Reactのコンポーネントを組み合わせて、シンプルなWebアプリを作ってみましょう。ここでは、タスクを登録し一覧することのできる「Todoアプリ」を、Reactを使って実装します。

 まずはWebページだけで動作するアプリから作り始め、そこからステップアップしてAjaxによるサーバへの通信処理も実装していきます。

コンポーネントの構成

 Todoアプリでは、次のユーザーインタフェースを提供する必要があります。

  • タスクを登録するフォーム(「form」エレメント)
  • タスクの一覧(「ul」エレメント)

 登録フォームには「input」と「button」のエレメントを含め、「button」がクリックされたら「input」の入力内容を新しいタスクとして登録し、タスクの一覧である「ul」の子要素「li」を表示します。

 コンポーネントは、入力フォームのコンポーネント(TodoForm)とタスクの一覧のコンポーネント(TodoList)をそれぞれ定義し、この2つのコンポーネントをアプリのコンポーネント(TodoApp)にまとめるような構成にします。

└── TodoApp
   ├── TodoForm
   └── TodoList

HTMLファイルの作成

 TodoアプリのためのHTMLファイルを作りましょう。ここまで試してきたものと同じ構成になるので、既存のHTMLファイルを使う形でも構いません。

<!-- コアライブラリ -->
<script src="https://fb.me/react-15.2.0.js"></script>
<!-- DOMライブラリ -->
<script src="https://fb.me/react-dom-15.2.0.js"></script>
<!-- Babelライブラリ -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.10.3/babel.min.js"></script>
 
<!-- 管理対象の要素 -->
<div id="content"></div>
 
<script type="text/babel">
// この中に記述していく
</script>

フォームのコンポーネントの定義

 まずはフォームのコンポーネントの定義から始めましょう。次のコードを書いてください。

var TodoForm = React.createClass({
	render: function() {
		return <form onSubmit={this.props.onSubmit}>
		         <input onChange={this.props.onChange} value={this.props.text} />
		         <button>Add</button>
		       </form>
	}
});

 この「TodoForm」の「render」では、「input」と「button」を持った「form」を返しています。

 「input」に入力されたイベントのハンドリングと「button」のクリックイベントのハンドリングは、このコンポーネントを元にしたエレメントを生成するときに属性として指定します。コンポーネントの中では「props.onSubmit」などのように参照することができます。「props.onSubmit」と「props.onChange」の実装は「TodoApp」に記述します(後述)。

 また「input」の「value」には「props.text」としています。これについても「TodoApp」の中で実装します。このようにしているのは「input」の変更内容を「TodoApp」で保持できるようにしつつ、「input」自体に反映できるようにするためです。

リストのコンポーネントの定義

 次に、タスクの一覧を表示するリストのコンポーネントを定義しましょう。次のコードを書いてください。

var TodoList = React.createClass({
	render: function() {
		var createItem = function(item) {
			return <li key={item.id}>{item.text}</li>;
		};
		return <ul>{this.props.items.map(createItem)}</ul>;
	}
});

 この「TodoList」の「render」では「ul」を返しています。「ul」の要素となる「li」はタスクの数だけ用意する必要があります。ここでは「TodoApp」から「props.items」で指定される想定で、「props.items」の数だけ「li」を生成するようにしています。

 「props.items」の要素は「id」と「text」をキーに持つシンプルなオブジェクトを想定しています。このうち「id」は最低限の実装では必要ないようにも見えますが、Reactの「li」のエレメントは属性に「ul」の中で一意となる「key」を指定しなければいけない制約があるため、必要となっています。「id」自体の採番は「TodoApp」に記述します(後述)。

アプリのコンポーネントの定義

 次に、フォームとリストをまとめる、アプリのコンポーネントを定義しましょう。次のコードを書いてください。

var TodoApp = React.createClass({
	// 【1】初期値の指定
	getInitialState: function() {
		return {items: [], text: ''};
	},
	// 【2】フォームの入力テキスト変更イベントのハンドラ
	handleChange: function(e) {
		this.setState({text: e.target.value});
	},
	// 【3】フォームのボタンクリックイベントのハンドラ
	handleSubmit: function(e) {
		e.preventDefault();
		var newItems = this.state.items.concat({
			id: Date.now(),
			text: this.state.text
		});
		this.setState({items: newItems, text: ''});
	},
	// 【4】フォームとリストを子エレメントに持つ div を返す
	render: function() {
		return <div>
			<TodoForm
			  onChange={this.handleChange}
			  onSubmit={this.handleSubmit}
			  text={this.state.text} />
			<TodoList items={this.state.items} />
		</div>
	}
});
ReactDOM.render(<TodoApp />, content);

 まず【1】では状態を示す「state」の初期値を指定しています。「TodoApp」ではタスクの配列である「items」と新規登録されるタスクの文字列である「state.text」を持たせます。

 【2】と【3】は「TodoForm」で発火するイベントのハンドラです。【2】は「input」の「onChange」のハンドラです。ここでは「setState」で「text」を変更しています。

 【3】は「form」の「onSubmit」のハンドラです。まずは「preventDefault」を呼び出すことで、イベントをキャンセルしています。その上で新しいタスクの文字列を「state.items」の先頭に追加し、「setState」で「state.items」の状態を変更しています。

 【4】は「TodoForm」と「TodoList」を子エレメントとして持つ「div」を返しています。「TodoForm」の定義では、属性として「props.handleChange」と「props.handleSubmit」、そして「props.text」を持たせていました。ここでは、それぞれ「handleChange」と「handleSubmit」、そして「state.text」を渡しています。また「TodoList」の定義では属性として「props.items」を持たせていたので、「state.items」を渡しています。

 ここまで実装すると、Webページ内で完結するTodoアプリとして動作させることができます。作成したHTMLファイルをブラウザで開き、動作を確認してみてください。

Todoアプリの動作確認

Ajaxを使ってWebサーバと通信させる

 次に、TodoアプリのタスクをWebサーバで管理するように修正してみましょう。

 まず、タスクを管理するWebサーバを用意する必要があります。次のAPIを提供するWebサーバを構築しましょう。

  • GETリクエストでタスクの一覧を返すAPI
  • POSTリクエストでタスクを新規登録し、タスクの一覧を返すAPI

 Webアプリの実装方法は本記事では割愛しますが、Node.jsで動作するWebアプリをGitHubで公開しています。こちらのソースコードを使って、ローカルでWebサーバを構築しましょう。

 初めに、Node.jsをインストールします。インストール方法については、次の記事を参考にしてください。

 ソースコードをcloneしたら、clone先のディレクトリのルートで依存関係のあるライブラリをインストールしましょう。次のコマンドを実行してください。

$ npm install

 次に、Webアプリケーションを起動します。

$ npm start

 以上で、ローカルで動作するWebサーバを構築できました。

 HTMLファイルに戻りましょう。Ajax通信を行うため、jQueryのライブラリを導入します。次の「script」タグを追加してください。

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.js"></script>

 次に「TodoApp」のコンポーネントの実装を次のコードに書き換えてください。

var TodoApp = React.createClass({
	getInitialState: function() {
		return {items: [], text: ''};
	},
	// 【1】追加する
	componentDidMount: function() {
		$.ajax({
			url: this.props.url,
			dataType: 'json',
			cache: false,
			success: function(data) {
				this.setState({items: data});
			}.bind(this),
			error: function(xhr, status, err) {
				console.error(this.props.url, status, err.toString());
			}.bind(this)
		});
	},
	handleChange: function(e) {
		this.setState({text: e.target.value});
	},
	// 【2】修正する
	handleSubmit: function(e) {
		e.preventDefault();
		var item = { text: this.state.text };
		$.ajax({
			url: this.props.url,
			dataType: 'json',
			type: 'POST',
			data: item,
			success: function(data) {
				this.setState({items: data, text: ''});
			}.bind(this),
			error: function(xhr, status, err) {
				console.error(this.props.url, status, err.toString());
			}.bind(this)
		});
	},
	render: function() {
		return <div>
			<TodoForm
			  onChange={this.handleChange}
			  onSubmit={this.handleSubmit}
			  text={this.state.text} />
			<TodoList items={this.state.items} />
		</div>
	}
});
// 【3】修正する
ReactDOM.render(<TodoApp url="http://localhost:3000/api/items" />, content);

 【1】では、新たにライフサイクルイベントのハンドラである「componentDidMount」を追加しています。このメソッドが呼ばれるタイミングは前述している通り、コンポーネントのエレメント自体がDOMノードに追加されたときです。

 ここでは「$.ajax」を呼ぶことで、タスクの一覧を取得するAPIをリクエストするHTTP通信を行っています。「url」に指定している「props.url」がエンドポイントになります。「props.url」の指定は後述(【3】)のコードで行います。処理が完了したときに呼び出される「success」では「setState」を呼び出し、「state.item」をレスポンスボディーに変更しています。

 【2】は、タスクを登録する処理について、タスクを登録するAPIをリクエストするHTTP通信に修正しています。「$.ajax」に渡す「data」には、「text」を持つオブジェクトを指定しています。処理が完了したときに呼び出される「success」では「setState」を呼び出し、「state.item」をレスポンスボディーに変更しています。

 【3】では【1】と【2】の中で実装している「$.ajax」に渡す「url」に指定する、エンドポイントとなる属性「props.url」を追加しています。ローカルのWebサーバの場合は「http://localhost:3000/api/items」になります。

 以上で実装は終わりです。ブラウザで動作確認してみてください。

「状態」が最も重要な考え方

 Reactはユーザーインタフェースに関する機能を提供したフレームワークです。幾つかの実装例を通して、Reactの機能自体は非常にシンプルであることが分かったのではないかと思います。その中でも「状態」が最も重要な考え方です。状態の変化に応じて効率的に処理できる点が、最大の特徴ではないでしょうか。

 本稿がReactを試してみるきっかけになればうれしい限りです。

著者紹介

諏訪 悠紀(すわ ゆうき)

クラスメソッド株式会社に所属。iOS/Androidアプリの開発およびデザイン、AWSを活用したWebアプリケーション開発などに従事。技術ブログ「Developers.IO」をはじめ、@IT、技術評論社、日経ソフトウエアなどのメディアで最新の技術情報を発信している。


前のページへ 1|2|3       

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。