React入門８: テキストの実装

# はじめに
私自身の復習兼、備忘録的な意味もあり、複数回の記事に渡って React を用いてマルバツゲーム(三目並べ)を開発していきたいと思います。
- [React](https://ja.react.dev/)
- [CodeSandbox(公式)](https://codesandbox.io/)
- [CodeSandbox(非公式)](https://csb-jp.github.io/)

## シリーズの一覧
1. [React入門１: 環境構築 [オンライン版]](https://qiita.com/banana_bancho/items/64fa6ccb58101eadbb63)
   - [React入門1.5: 環境構築 [ローカル版-番外編]](https://qiita.com/banana_bancho/private/30f5d9d2c5eea00ac11c)
1. [React入門２: 盤面の作成](https://qiita.com/banana_bancho/private/c50495b5d4c30555cec3)
1. [React入門３: インタラクションの実装](https://qiita.com/banana_bancho/private/557d14b2413cb0317ce4)
1. [React入門４: リファクタリング [リフトアップ編]](https://qiita.com/banana_bancho/private/81730326c004e52c6aad)
1. [React入門５: リファクタリング [インタラクション編]](https://qiita.com/banana_bancho/private/3f20d23aadb49fe233f8)
1. [React入門６: 手番の実装](https://qiita.com/banana_bancho/private/6bed838f67e575f58b72)
1. [React入門７: ゲームの勝利判定](https://qiita.com/banana_bancho/private/c0eca4e9348edb430561)
1. React入門８: テキストの実装 (今回)
1. [React入門９: タイムトラベル(1)](https://qiita.com/banana_bancho/private/c674872e32fcb52bb2a0)
1. [React入門10: タイムトラベル(2)](https://qiita.com/banana_bancho/private/861398f1fa4efd378938)
1. [React入門11: タイムトラベル(3)](https://qiita.com/banana_bancho/private/0d3cf7e1b1ebbf20c5e0)


# 目的について
## 全体の目的
React公式のチュートリアルで公開されているマルバツゲームを 3x3 のマスで実装していきます。

https://ja.react.dev/learn/tutorial-tic-tac-toe

## 今回の目的
ゲームの状況を示すテキストを実装していきます。また、前回作成した ```calculateWinner()``` 関数を使って、決着がついた場合の勝者も表示できるようにします。

# テキストの実装
次のページで、前回のソースファイルを確認できます。

https://codesandbox.io/p/sandbox/react-tutorial7-twqc8t

- 前回の内容はコチラから！

https://qiita.com/banana_bancho/private/c0eca4e9348edb430561

## テキストの表示
App.js の ```Board``` コンポーネントの ```return``` 文に ```<div>``` 要素を加えて、盤面の上にテキストを表示させてみましょう。次の要件を満たすようにコーディングをしてみます。
- 盤面の上に文字を表示するには ```return``` 文のどこに ```<div>``` 要素を加えればよいか
- 「 ```ゲームの状況``` 」というテキストを表示する

```jsx:App.js の Board コンポーネント
export default function Board() {

  /* 省略 */

  return (
    <>
      <div>ゲームの状況</div>
      <div className="board-row">
        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />
        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />
        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />
      </div>
      <div className="board-row">
        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />
        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />
        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />
      </div>
      <div className="board-row">
        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />
        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />
        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />
      </div>
    </>
  );
}
```

次に動作結果を示します。これで盤面の上に「ゲームの状況」というテキストを表示できるようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2844961/fc129e0e-423a-fe2e-1f1f-102d25852dde.png)

## 手番の表示
テキストで現在のプレイヤーが「X」と「O」のどちらであるかを表示していきます。

[第6回の記事](https://qiita.com/banana_bancho/private/6bed838f67e575f58b72#oの表示) で定義した、手番を管理している ```state``` 型の ```xIsNext``` 変数を使って、現在のプレイヤーを取得しましょう。この変数は、```true``` のときに「X」、```false``` のときに「O」のプレイヤーを表しています。

従って、テキスト( ```status``` 変数)を次のように制御すれば、操作中しているプレイヤーを示すことができます。
``` js: 手番を表示するテキストの制御
  let status = "プレイヤー: ";

  if (xIsNext) {
    status += "X";
  } else {
    status += "O";
  }
```

では、App.js の ```Board()``` コンポーネントに次の変更を加えましょう。
- 手番を表示するテキストの制御するプログラムを実装する
    - ゲームの状況を文字列で示す ```status``` 変数を宣言する
    - ```status``` 変数の内容を手番 (```xIsNext``` 変数) に応じて更新する
- ```return``` 文にある　```<div>``` 要素で手番を表示させる
    - [JSX の文法に注意しましょう](https://qiita.com/banana_bancho/items/c50495b5d4c30555cec3#波括弧--の文法的意味)
``` js: App.js の Board コンポーネント
export default function Board() {
  /* 省略 */
  let status = "プレイヤー: ";

  if (xIsNext) {
    status += "X";
  } else {
    status += "O";
  }

  return (
    <>
      <div>{status}</div>

      <div className="board-row">
      /* 省略 */
      </div>
    </>
  );
```

動作結果を次に示します。これで、空白のマスをクリックした後に、テキストが変更されるようになりました。

![output_video.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2844961/ee4bec96-260f-3cd0-95f8-4be68e30c4d6.gif)


### 条件 (三項) 演算子の利用
JavaScriptの文法的な内容になります。動作結果に関する変更はないので、[勝者の表示](#勝者の表示) まで飛ばしても大丈夫です。

先ほどのコードを、条件 (三項) 演算子を用いることで短くまとめていきます。まずは、見慣れた ```if-else``` 文の構文を示します。
```js: if-else 文
if (条件) {
  真のときに実行する処理;
} else {
  偽のときに実行する処理;
}
```

これを条件 (三項) 演算子によって表現すると次の構文になります。

```js: 
条件 ? 真のときに返す値 : 偽のときに返す値;
```

詳しい内容を調べたい方は次のリファレンスをご確認ください。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Conditional_Operator

従って、手番を表示するテキストの制御は、次のようなコードで表すことができます。

``` js: 手番を表示するテキストの制御 (簡略後)
  let status = "プレイヤー: " + (xIsNext ? "X" : "O");
```

余談ですが、このようにコードを省略することをショートハンドというみたいです。

## 勝者の表示
ゲームの決着がついたときに勝者を表示できるようにしていきます。App.js の ```Board``` コンポーネントに次の変更を加えましょう。
- 勝者を示す ```winner``` 変数を宣言する
    - 初期値を勝利判定 (```calculateWinner()``` 関数) の結果とする
- 勝利判定の結果に応じてテキストの内容を変更する
    - 勝者がいる場合は、その印を表示する
        - 「X」が勝ったとき、「勝者: X」
        - 「O」が勝ったとき、「勝者: O」
    - 勝者が決まっていない場合は、次のプレイヤーを表示する
```jsx: App.js
import { useState } from "react";
import "./styles.css";

function Square({ value, onSquareClick }) {
  return (
    <button className="square" onClick={onSquareClick}>
      {value}
    </button>
  );
}

export default function Board() {
  const [squares, setSquares] = useState(Array(9).fill(null));
  const [xIsNext, setXIsNext] = useState(true);

  const winner = calculateWinner(squares);
  let status;

  if (winner) {
    status = "勝者: " + winner;
  } else {
    status = "プレイヤー: " + (xIsNext ? "X" : "O");
  }

  function handleClick(i) {
    if (squares[i]) {
      return;
    }

    const nextSquares = squares.slice();

    if (xIsNext) {
      nextSquares[i] = "X";
    } else {
      nextSquares[i] = "O";
    }
    setSquares(nextSquares);
    setXIsNext(!xIsNext);
  }

  return (
    <>
      <div>{status}</div>
      <div className="board-row">
        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />
        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />
        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />
      </div>
      <div className="board-row">
        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />
        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />
        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />
      </div>
      <div className="board-row">
        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />
        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />
        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />
      </div>
    </>
  );
}

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 = lines[i][0];
    const b = lines[i][1];
    const c = lines[i][2];

    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      return squares[a];
    }
  }
  return null;
}

```

動作結果を次に示します。これで、操作するプレイヤーだけでなく、勝者も表示できるようになりました。

![勝者の判定.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2844961/12d5a152-470f-495c-e522-23b55398087f.gif)



これでゲームのシステムは完成しました！

# おわりに
今回は、ゲームの状況を示すテキストを実装しました。これによって、マルバツゲームの主要な機能は完成です！お疲れ様でした。次のページにソースファイルを示します。

https://codesandbox.io/p/sandbox/react-tutorial8-8vprcd

:::note info
今後について
ここまで、マルバツゲームを開発していきましたが、まだまだ機能を追加する余地があります。例えば、公式チュートリアルにはまだ続きがあり、過去の手番に戻すタイムトラベル機能について解説されています。ご興味がある方は、上記のソースファイルを改変して次のページをご参照ください。(~~筆者の方でも記事にまとめるかもしれません...~~ ← 9回目以降の記事にまとめました)

[チュートリアル：三目並べ – React [タイムトラベルの追加]](https://ja.react.dev/learn/tutorial-tic-tac-toe#adding-time-travel)
:::

次回以降から、ゲームにタイムトラベルの機能を追加していきます。

https://qiita.com/banana_bancho/private/c674872e32fcb52bb2a0