今回は、Reactでアプリの作成をしていきたいと思います!
ゴールは、Reactチュートリアルの三目並べゲームを完成させることです。
それでは早速取り掛かっていきましょう。
Reactとは?
そもそもReactとはなにか?
Reactは、Facebook社が開発したJavaScriptライブラリになります。
詳しくは触れないので気になる方は調べてみましょう。
Reactアプリ作成 ~準備~
Reactチュートリアルに沿って、進めていきます。
チュートリアルでは、スターターコードを開きコードの修正を行うことができます。
今回は、ローカル開発環境で進めていきたいと思います。
Reactのアプリケーションを動かすには、Node.jsが必要となります。
インストールされていない場合はインストールしましょう。
homebrew(パッケージ管理ツール)を使用している場合は、以下のコマンドでインストールすることが可能です。
brew install nodejs
インストールすることができたら、アプリケーションを作成していきましょう。
以下のコマンドでアプリケーションを作成します。
npx create-react-app 作成したいアプリケーション名
例) npx create-react-app proto_app
作成することができたら、チュートリアルの記述に沿って、src/内にあるファイルすべてを削除します。
# アプリケーションのディレクトリに移動
cd 作成したアプリケーション
# srcのディレクトリに移動
cd src
# すべてのファイルを削除
rm -f *
# アプリケーションのディレクトリに移動
cd ..
削除することができたら、チュートリアルで指定されている以下のファイルをsrc/内に作成します。
- index.css
- index.js
作成したファイルに用意されているコードを記述します。
index.jsに関しては、以下の記述も必要となります。
ファイルの先頭に以下の3行を記述しましょう。
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
記述することができたら、npm startを実行し、ブラウザでlocalhost:3000にアクセスしましょう。
ブラウザが以下のように表示されていれば大丈夫です。
ここまでが準備になります。
準備ができたら次に進みましょう。
Reactアプリ作成 ~中身の確認、データの受け渡し~
src/index.jsの中身を確認していきましょう。
3つのReactコンポーネントがあることが確認できます。
- Square
- Board
- Game
BoardコンポーネントからSquareコンポーネントに値を渡してみます。
index.jsを以下のようにコード変更します。
class Square extends React.component {
render() {
return (
<button className="square">
{this.props.value} // 変更点、値を受け取る記述
</button>
)
}
}
class Board extends React.Component {
renderSquare(i) {
return <Square value={i}/>; // 変更点、値を渡す記述
}
}
記述することができたら、 npm start でサーバーを起動して確認してみましょう。
以下の画像のように表示されているはずです。
値の受け渡しができました。
Reactでは親から子へ「propsを渡す」ことで情報を渡すことができます。
確認することができたら次に進みましょう。
Reactアプリ作成 ~OXを表示、勝者の判別~
Squareコンポーネントがクリックされた場合に”X”が表示されるようにしていきます。
index.jsの記述を以下のようにしましょう。
class Square extends React.component {
render() {
return (
<button className="square" onClick={function() {alert('クリック'); }}> //Squareコンポーネントをクリックしたときにalertが表示されるように記述
{this.props.value}
</button>
)
}
}
記述することができたら、ブラウザでSquareコンポーネントをクリックしてみましょう。
画像のようにalert表示が出てきたらOKです!
次にSquareコンポーネント(マス)がクリックされたときに、どのマスがクリックされたのかを覚えさせ、マスに”X”を表示させていきます。
コンポーネントが何かを”覚えるため“には、「state」を使用します。
index.jsに記述していきましょう。
class Square extends React.component {
// ここから
constructor(props) {
super(props);
this.state = {
value: null,
};
}
// ここまで
render() {
return (
<button className="square" onClick={function() {alert('クリック'); }}>
{this.props.value}
</button>
)
}
}
コンストラクタでthis.stateを設定することで状態を持つことができるようになります。
現在のSquareコンポーネントの情報をthis.stateに保存して、マスがクリックされたらそれを変更していきます。
Squareのrenderメソッドを書き換えていきます。
index.jsを以下のように記述しましょう。
class Square extends React.component {
constructor(props) {
super(props);
this.state = {
value: null,
};
}
render() {
return (
<button className="square" onClick={() => this.setState({value: 'X'})}> // 記述変更
{this.state.value} // 記述変更
</button>
)
}
}
onClickハンドラ内でthis.setStateを呼び出すことで、マスがクリックされたら”X”が表示されるようになります。
ブラウザで実際の挙動を確認してみましょう。
クリックした箇所に”X“が表示されることが確認できたら次に進みましょう。
あとは、”O“と”X“が交互に表示されるようにして、どちらのプレーヤーが勝利したか判定できるようにしていきます。
ゲームの状態を管理するために、親になるBoardコンポーネントで保持するようにしていきます。
index.jsのBoardに記述を追加しましょう。
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
squares: Array(9).fill(null),
};
}
renderSquare(i) {
return (
<Square
value={this.state.squares[i]}
onClick={() => this.handleClick(i)}
/>;
)
}
}
記述することができたら、Squareも記述を変更していきます。
class Square extends React.component {
render() {
return (
<button
className="square"
onClick={() => this.props.onClick()}
>
{this.props.value}
</button>
)
}
}
SquareがクリックされるとBoardから渡されたonClick関数がコールされます。
イベントを表すには、”on[イベント]”、呼び出しには”handle[イベント]”という名前を付けます。
次に、handleClickを定義していきます。
Boardクラスに記述を追加しましょう。
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
squares: Array(9).fill(null),
};
}
// ここから
handleClick(i) {
const squares = this.state.squares.slice();
squares[i] = 'X';
this.setState({squares: squares});
}
// ここまで
renderSquare(i) {
return (
<Square
value={this.state.squares[i]}
onClick={() => this.handleClick(i)}
/>;
)
}
}
マス目の状態は、Squareコンポーネントではなく、Boardコンポーネント内に保存されている状態になります。
Board側で情報を保持することで、どちらが勝者か判断できるようになります。
ここでSquareコンポーネントを関数コンポーネントに変換していきます。
関数コンポーネントとは、renderメソッドだけを有して自分の state を持たないコンポーネントを、よりシンプルに書くための方法です。
React.Component を継承するクラスを定義する代わりに、props を入力として受け取り表示すべき内容を返す関数を定義します。
関数コンポーネントはクラスよりも書くのが楽であり、多くのコンポーネントはこれで書くことができます。
Squareクラスを関数に書き直しましょう。
this.propsはpropsに書き換えます
function Square(props) {
return (
<button
className="square"
onClick={props.onClick}
>
{props.value}
</button>
);
}
次に、”O“が表示されるようにしていきましょう。
Boardクラスに記述しましょう。
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
squares: Array(9).fill(null),
xIsNext: true, // 記述を追加
};
}
handleClick(i) {
const squares = this.state.squares.slice();
squares[i] = this.state.xIsNext ? 'X' : 'O'; // 記述を追加、三項演算子を使用して条件を記述
this.setState({
squares: squares,
xIsNext: !this.state.xIsNext, // 記述追加
});
}
renderSquare(i) {
return (
<Square
value={this.state.squares[i]}
onClick={() => this.handleClick(i)}
/>;
)
}
}
どちらのプレーヤの手番なのかを決める xIsNext
(真偽値)が反転され、ゲームの状態が保存されます。Board の handleClick
関数を書き換えて xIsNext
の値を反転させるようにします。
こちらの変更をしたことで、クリックするたびに”X“と”O“が交互に表示されます。
ブラウザでクリックしたら交互に表示されるか確認してみましょう。
すべてのマスを埋めると以下の画像のようになるかと思います。
最後に、「ゲーム勝者の判定」ができるようにしていきましょう。
index.jsの末尾にヘルパー関数をコピーして貼り付けていきましょう。
function calculateWinner(squares) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
}
}
return null;
}
こちらの関数(calculateWinner
)によって、勝者を適切に判断することができます。
“X“、”O“あるいはnullを返してくれます。
こちらの関数を呼び出していきましょう。
更に、ゲームの決着が付いている場合やクリックされたマス目がすでに埋まっている場合に、returnするようにしていきます。
index.jsにコードを追記しましょう。
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
squares: Array(9).fill(null),
xIsNext: true,
};
}
handleClick(i) {
const squares = this.state.squares.slice();
if (calculateWinner(squares) || squares[i]) { // 決着がついている場合、マスがクリックされていた場合の処理
return;
}
squares[i] = this.state.xIsNext ? 'X' : 'O';
this.setState({
squares: squares,
xIsNext: !this.state.xIsNext,
});
}
renderSquare(i) {
return (
<Square
value={this.state.squares[i]}
onClick={() => this.handleClick(i)}
/>;
)
}
render() {
const winner = calculateWinner(this.state.squares) // 関数の呼び出し
let status;
if (winner) {
status = 'Winner: ' + winner; // 勝者の表示
} else {
status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
}
}
}
記述することができたら、ブラウザで確認してみましょう。
これで「三目並べゲーム」完成です!
チュートリアルでは、このあとタイムトラベル機能を追加していきます。
興味のあるかたは、ぜひ実装にチャレンジしてみてください!
最後までご覧いただきありがとうございました。