サイトトップ

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

HTML5テクニカルノート

React + ES6 入門 01: ゲームの盤面をつくる


ReactはFacebook社が開発している、今もっとも注目されているJavaScriptフレームワークです。HTMLの要素を動的につくり上げて、ページに表示することができます。ただ、JSXという独特な構文を使い、公式サイトの解説にはECMAScript 6(ECMAScript 2015)が用いられるようになりました。「React + ES6 入門」では「TUTORIAL」をもとに、それらの構文も含めてご説明します。Reactをはじめて使う方は、先に「React + ES6: まずは動かしてみる」を読まれるとよいでしょう。

01 コードを書く前に素材を整える

「TUTORIAL」でつくるのは、マルバツゲームです。できあがりは、CodePenに掲げられています(サンプル001)。JavaScriptコードを見ると、HTMLのタグ<>がスクリプトの中に書き込まれています。このReact特有の構文が「JSX」です(「いまさら聞けないReact、Virtual DOM、JSX超入門」の「JSXとBabelの使い方」参照)。もっとも、JSXはブラウザがわかるように変換(コンパイル)しなければなりません。CodePenのJavaScriptコードの上に「Babel」というボタンがあります。このBabelがコンパイラなのです。ただし、ダウンロードして試すには、手もとにJSXのコンパイル環境を整えなければなりません。また、学習に必ずしもつながらないコードも含まれているようです。そこで、できるだけ簡素な素材を揃えつつ進めることにします。

サンプル001■Tic Tac Toe

See the Pen Tic Tac Toe by Eric Nakagawa (@ericnakagawa) on CodePen.

HTMLドキュメントはつぎのとおりです。はじめのふたつの<script>要素が、ReactのJavaScriptライブラリを読み込みます。CDNを使っていますので、ローカルにダウンロードしなくて構いません。3つめの<script>要素には、babel-standaloneを読み込んでいます。JSXをブラウザ上でコンパイルするBabelのJavaScriptライブラリです。ReactのJSXコードを書くJavaScriptファイル(script.js)は別につくって、フォルダ(src)に納めることにします。このファイルを読み込む<script>要素には、type属性に"text/babel"を与えてください。<body>には<div>要素をひとつ加え、JavaScriptコードから参照するid属性(container)を与えました。

sample.html

<!doctype html>
<html>
<head>
	<meta charset="UTF-8">
	<title>Sample</title>
	<link rel="stylesheet" href="css/style.css">
	<script src="https://unpkg.com/react@15/dist/react.min.js"></script>
	<script src="https://unpkg.com/react-dom@15/dist/react-dom.min.js"></script>
	<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>

<body>
	<div id="container"></div>
	<script src="src/script.js" type="text/babel"></script>
</body>
</html>

ランタイムのコンパイルはコードが手軽に試せるものの、ブラウザに負荷をかけるので効率はよくありません。公開のコンテンツでは、あらかじめ静的にコンパイルしておくのがよいでしょう(「React入門 06: ローカルサーバーの立ち上げとJSXのコンパイル」02「BebelのReact presetでJSXを標準のJavaScriptコードにコンパイルする」)。その場合、babel-standaloneは要らなくなります。

スタイルシートのCSSファイル(style.css)は、前掲サンプル001のCodePenからそのまま使います(フォルダcssに納めます)。つぎのコードをコピーしても構いません。

style.css

body {
	font: 14px "Century Gothic", Futura, sans-serif;
	margin: 20px;
}
ol, ul {
	padding-left: 30px;
}
.board-row:after {
	clear: both;
	content: "";
	display: table;
}
.status {
  margin-bottom: 10px;
}
.square {
	background: #fff;
	border: 1px solid #999;
	float: left;
	font-size: 24px;
	font-weight: bold;
	line-height: 34px;
	height: 34px;
	margin-right: -1px;
	margin-top: -1px;
	padding: 0;
	text-align: center;
	width: 34px;
}
.square:focus {
	outline: none;
}
.kbd-navigation .square:focus {
	background: #ddd;
}
.game {
	display: flex;
	flex-direction: row;
}
.game-info {
	margin-left: 20px;
}

02 マス目の要素をつくってページに差し込む

JavaScriptファイル(script.js)に、JSX構文でReactのコードを書きます。要素をHTMLドキュメントに差し込むのはReactDOM.render()メソッドです。第1引数がつくる要素、第2引数は差し込む先の親要素です


ReactDOM.render(つくる要素, 差し込み先親要素)

第1引数のつくる要素は、JSXでタグをじかに書き込んでも構いません。けれど、コンポーネントとしてクラスで定めることもできます。ECMAScript 6(ECMAScript 2015)には、クラス構文が採り入れられました。コンポーネントは、classキーワードを用いてつぎのように定義します。コンポーネントのクラスは、extendsキーワードでReact.Componentを基本クラスとして継承しなければなりません。そして、render()メソッドからJSXで書いた要素の定めを返します[*1]。クラスに備えるメソッドは、関数と同じかたちです。ただし、キーワードfunctionは添えません。


class クラス名 extends React.Component {
	render() {
		return (
			要素の定め
		);
	}
}

以下のコード001は、マス目の要素(<button>)をひとつつくって、ページの親要素(id属性container)に差し込みます。ReactDOM.render()メソッドの第1引数に渡すJSXのタグには、クラス名(Square)を与えました。なお、JSXのタグに定めるクラスは、オブジェクトとしての要素のclassNameプロパティに加えます。class属性ではありませんのでご注意ください。

図001■ページに描かれたマス目

図001

コード001■ページにマス目の要素をひとつ表示する

script.js

class Square extends React.Component {
	render() {
		return (
			<button className="square">
			</button>
		);
	}
}
ReactDOM.render(
	<Square />,
	document.getElementById('container')
);

[*1] React.Componentは抽象クラスです。継承したサブクラスは、render()メソッドを備えなければなりません(「React.Component」の「Overview」参照)。

03 9マスの盤面を組み立てる

ゲームの盤面はマス目9つで組み立てます。盤面はつぎのように新たなクラス(Board)で定めました。JSXの構文の中に波かっこ{}を使うと、JavaScriptの式が書けます。つまり、変数(プロパティ)を参照したり、関数(メソッド)が呼び出せるのです。render()メソッドは、const宣言した定数(status)の値を<div>要素のテキストとして差し込み、3コマから1行をつくる<div>要素の中からメソッド(renderSquare())を呼び出します。そして、メソッドからマス目(Square)の要素をひとつ受け取って加えているのです。


class Board extends React.Component {
	renderSquare(i) {
		return <Square value={i} />;
	}
	render() {
		const status = 'Next player: X';
		return (
			<div>
				<div className="status">{status}</div>
				<div className="board-row">
					{this.renderSquare(0)}
					{this.renderSquare(1)}
					{this.renderSquare(2)}
				</div>
				<div className="board-row">
					{this.renderSquare(3)}
					{this.renderSquare(4)}
					{this.renderSquare(5)}
				</div>
				<div className="board-row">
					{this.renderSquare(6)}
					{this.renderSquare(7)}
					{this.renderSquare(8)}
				</div>
			</div>
		);
	}
}

マス目(Square)の要素を返すメソッド(renderSquare())は、引数(i)の値を属性(value)に与えています。属性は変数としてコンポーネントのpropsプロパティに納められます。すると、モジュール(Square)の要素の記述から、つぎのように波かっこ{}でプロパティを参照すれば、変数値が取り出せるのです。


class Square extends React.Component {
	render() {
		return (
			<button className="square">
				{this.props.value}
			</button>
		);
	}
}

ReactDOM.render()メソッドの呼び出しをつぎのように書き換えれば、組み立てられた9つのマス目それぞれに0からはじまる連番整数が示されます(図002)。


ReactDOM.render(
	// <Square />,
	<Board />,
	document.getElementById('container')
);

図002■9つのマス目に連番整数が示された

図002

04 クリックしたマス目に印をつける

ECMAScript 6には、アロー関数式=>が採り入れられました。名前のない関数がつぎのように書き替えられます。


// 名前のない関数
function(引数, ..., 引数) {return 式;}

// アロー関数式
(引数, ..., 引数) => 式

マス目のコンポーネント(Square)の要素に、onClickイベントのハンドラをアロー関数式で定めてみましょう[*2]。JSXのタグにイベントハンドラをつぎのように書き加えれば、クリックしたマス目の番号が警告ダイアログに示されます。


class Square extends React.Component {
	render() {
		return (
			<button className="square" onClick={() => alert(this.props.value)}>
				{this.props.value}
			</button>
		);
	}
}

コンポーネントのインスタンスにプロパティを定めたいときは、setState()メソッドを用います。呼び出すとき引数に渡すオブジェクトは、インスタンスのstateプロパティに納められるのです。マス目のコンポーネント(Square)の要素に加えたonClickイベントのハンドラを、つぎのように書き替えます。すると、クリックしたマス目の要素に「X」がつきます(図003)。stateプロパティの値ははじめはnullです。その場合、オブジェクトとして参照できません。そのため、論理演算子&&で存在をたしかめました


class Square extends React.Component {
	render() {
		return (
			<button className="square" onClick={() => this.setState({value: 'X'})}>
				{this.state && this.state.value}
			</button>
		);
	}
}

図003■クリックしたマス目にXがつく

図003

これで9マスのゲーム盤面ができ上がり、クリックしたマス目に「X」がつくようになりました。ここまでのスクリプトを、つぎのコード002にまとめます。合わせて以下のサンプル002にjsdo.itのコードを掲げました。JSXを使ったECMAScript 6のJavaScriptコードは[ES6]タブに書いています。

コード002■9マスのゲーム盤面をつくる

script.js

class Square extends React.Component {
	render() {
		return (
			<button className="square" onClick={() => this.setState({value: 'X'})}>
				{this.state && this.state.value}
			</button>
		);
	}
}
class Board extends React.Component {
	renderSquare(i) {
		return <Square value={i} />;
	}
	render() {
		const status = 'Next player: X';
		return (
			<div>
				<div className="status">{status}</div>
				<div className="board-row">
					{this.renderSquare(0)}
					{this.renderSquare(1)}
					{this.renderSquare(2)}
				</div>
				<div className="board-row">
					{this.renderSquare(3)}
					{this.renderSquare(4)}
					{this.renderSquare(5)}
				</div>
				<div className="board-row">
					{this.renderSquare(6)}
					{this.renderSquare(7)}
					{this.renderSquare(8)}
				</div>
			</div>
		);
	}
}
ReactDOM.render(
	<Board />,
	document.getElementById('container')
);

[*2] アロー関数式は、本体のthisが関数の定められたオブジォクトを参照します。名前のない関数ではthisを束縛しないため、つぎのように書かなければなりません。


render() {
	let _this = this;
	return (
		<button className="square" onClick={function() {alert(_this.props.value)}}>

		</button>
	);
}

サンプル002■React 15.5.3 + ES6: Board with nine squares


作成者: 野中文雄
作成日: 2017年4月9日


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