サイトトップ

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

HTML5テクニカルノート

Create React App 入門 01: 3×3のマス目をつくる


ReactはFacebook社が開発している、今もっとも注目されているJavaScriptフレームワークです(「JavaScript: フレームワーク React/Vue/Angularについて」参照)。DOM(Document Object Model)の要素をデータと関連づけて(データバインディング)、データの変化に応じてページを動的に構成します。ただ、なじみの少ない構文の知識や準備が求められるため、なじみのない方には少し始めにくいかもしれません。公式Reactサイトには「チュートリアル:React の導入」がありますので、これを読むのがひとつの手です。CodePenにでき上がりのコードも掲げられています(サンプル001)。

もっとも、コードはコンポーネント分けされてはいるものの、ひとつのJavaScriptファイルです。「Create React App」を使えば、モジュール分けされたシングルページアプリケーション(SPA)のひな形が簡単につくれます。そのひな形に手を加えてチュートリアルと同じマルバツゲームに仕上げようというのが、この「Create React App 入門」シリーズです。

サンプル001■Tic Tac Toe

See the Pen Tic Tac Toe by Dan Abramov (@gaearon) on CodePen.

01 Reactアプリケーションのひな形をつくる

まずは、Create React Appのインストールです。予めNode.js(npm)が入っていなければなりません。コマンドラインツールからnpmでつぎのコマンドを打ち込んでください。


npm install -g create-react-app

npx create-react-appコマンドにつづけてアプリケーション名(react-tic-tac-toeとしました)を入力すると、その名前でフォルダとアプリケーションのひな形がつくられます。


npx create-react-app react-tic-tac-toe

ひな形がつくられたら、アプリケーションのディレクトリに移って、npm startとコマンドを打てば、ローカルサーバーでひな形のページが開きます(図002)。ローカルサーバーを閉じたいときは[control]/[Ctrl] + [C]、再開には改めてnpm startを入力してください。


cd react-tic-tac-toe
npm start

図001■ひな形のReactアプリケーションのページ

図001

02 マス目のコンポーネントをつくってアプリケーションに差し込む

src/App.jsは大もとになるコンポーネントです。ひな形アプリケーションには、ほかのコンポーネントはまだありません。コンポーネントは新たにsrc/componentsというフォルダにまとめましょう。src/App.jssrc/App.cssとともに、このフォルダに移してください。

また、つぎの4つのファイルは使わないので、削除してかまいません。

アプリケーションのスタイルシートsrc/components/App.cssは、前掲サンプル001のCodePenのCSSコードをそのまま使います。つぎのコードからコピーしてもよいでしょう。

src/components/App.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;
}

src/index.cssのCSSはアプリケーションにあまり影響しません。bodyの定めだけ残しました。

src/index.css

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
} */

つぎの抜き書きが、モジュールsrc/index.jsで変更するコードです。HTMLのタグ<>がスクリプトの中に直に書き込まれています。この特有の構文が「JSX」です。ReactDOM.render()メソッドはReactの要素をHTMLドキュメントに差し込みます。第1引数がつくるコンポーネント(App)、第2引数は差し込む先の親要素です。コンポーネントはフォルダを移しましたので、importのパスは書き替えました。また、前述のとおりserviceWorkerは使いません。

src/index.js

//import App from './App';
import App from './components/App';
// import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));  // 変更なし

// serviceWorker.unregister();

ルートの関数コンポーネントsrc/components/App.jsは、つぎのように大幅に書き替えます。importに加えたSquareは、このあと定める新たな子コンポーネントです。コンポーネントの関数(App())は、JSXで書いた要素の定めを返します。この戻り値がコンポーネントとして描画される要素です。つまり、子コンポーネントが入れ子でページに差し込まれます。なお、class属性はclassNameに替えなければなりません。classECMAScript 2015の予約語になっているためでしょう。また、テンプレートは必ずひとつのルート要素にまとめてください。

src/components/App.js

import React from 'react';
import Square from './Square';
// import logo from '../logo.svg';
import './App.css';

function App() {
  return (
		/* <div className="App">
			<header className="App-header">
				<img src={logo} className="App-logo" alt="logo" />
				<p>
					Edit <code>src/App.js</code> and save to reload.
				</p>
				<a
					className="App-link"
					href="https://reactjs.org"
					target="_blank"
					rel="noopener noreferrer"
				>
					Learn React
				</a>
			</header>
    </div> */
    <div className="game">
    	<Square />
    </div>
	);
}

export default App;

新しい子の関数コンポーネントsrc/components/Square.jsの定めはつぎのとおりで、<button>要素をひとつもつだけです。ここではアロー関数式=>を用いました。functionによる定義と基本的に違いはありません。関数は引数(props)をひとつ受け取ります。今はまだ使いません。動きを確かめると、src/components/App.cssのスタイルにより、ページにはマス目がひとつ描かれます(図002)。

src/components/Square.js

import React from 'react';

const Square = (props) => {
	return (
		<button className="square">
		</button>
	);
};

export default Square;

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

図002

src/index.jsにはもう手を加えませんので、コード001に掲げます。

コード001■ページにReactのルートモジュールを加える

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App';

ReactDOM.render(<App />, document.getElementById('root'));

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

マルバツゲームの盤面は3×3の9マスです。つぎのコード002の新たなコンポーネントsrc/components/Board.jsで、マス目の子コンポーネント(Square)を使って組み立てました。JSXの構文の中に波かっこ{}を使うと、JavaScriptの式が書けます。つまり、変数(プロパティ)を参照したり、関数(メソッド)が呼び出せるのです。関数コンポーネントが返すテンプレートでは、3コマから1行をつくる<div>要素の中から関数(renderSquare())を呼び出しています。そして、マス目(Square)の子コンポーネントをそれぞれ受け取って加えているのです。関数の引数(i)が子コンポーネントに、波かっこ{}で属性(value)の値として与えられていることにご注目ください。

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

src/components/Board.js

import React from 'react';
import Square from './Square';

const Board = (props) => {
	const renderSquare = (i) =>
		<Square value={i} />;
	return (
		<div>
			<div className="board-row">
				{renderSquare(0)}
				{renderSquare(1)}
				{renderSquare(2)}
			</div>
			<div className="board-row">
				{renderSquare(3)}
				{renderSquare(4)}
				{renderSquare(5)}
			</div>
			<div className="board-row">
				{renderSquare(6)}
				{renderSquare(7)}
				{renderSquare(8)}
			</div>
		</div>
	);
};

export default Board;

テンプレートでコンポーネントの属性に渡された値を受け取るのが、関数コンポーネントの引数(props)です(「コンポーネントとprops」参照)。属性をプロパティとして参照すれば、値が取り出せます。子コンポーネントsrc/components/Square.jsは、つぎのようにマス目の中に属性(value)の値をテキストとして示すようにしてみました。

src/components/Square.js

const Square = (props) => {
	return (
		<button className="square">
			{props.value}
		</button>
	);
};

ルートコンポーネントsrc/components/App.jsは、差し込む子コンポーネントをマス目(Square)から盤面(Board)に替えます。これで、3×3の9つのマス目が描かれ、それぞれに連番整数が示されるはずです(図003)。

src/components/App.js

// import Square from './Square';
import Board from './Board';

function App() {
  return (
    <div className="game">
    	{/* <Square /> */}
    	<Board />
    </div>
	);
}

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

図003

書き上がったマス目(src/components/Square.js)とルート(src/components/App.js)のコンポーネントは、つぎのコード003のとおりです。併せて、動きとそれぞれのモジュールの中身が確かめられるように、以下のサンプル002をCodeSandboxに掲げました。

コード003■3×3のマス目でゲーム盤面をつくる

src/components/Square.js

import React from 'react';

const Square = (props) => {
	return (
		<button className="square">
			{props.value}
		</button>
	);
};

export default Square;

src/components/App.js

import React from 'react';
import Board from './Board';
import './App.css';

function App() {
  return (
    <div className="game">
    	<Board />
    </div>
	);
}

export default App;

サンプル002■Create React App: Tic Tac Toe 01

Create React App 入門


作成者: 野中文雄
作成日: 2019年12月30日


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