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入門６: 手番の実装 (今回)
1. [React入門７: ゲームの勝利判定](https://qiita.com/banana_bancho/private/c0eca4e9348edb430561)
1. [React入門８: テキストの実装](https://qiita.com/banana_bancho/private/b841b1ce38482b31753c)
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

## 今回の目的
マスが「X」だけでなく「O」も表示できるようにして、手番(ターン)を実装していきます。

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

https://codesandbox.io/p/sandbox/react-tutorial5-xsx53w

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

https://qiita.com/banana_bancho/items/3f20d23aadb49fe233f8

## 「O」の表示
App.js の ```Board``` コンポーネントに次の変更をしましょう。
- 手番を表す変数を作成する
    - ```state``` 型の変数 ```xIsNext``` を宣言する
        - 初期値を ```ture``` とする
        - set関数の名称を ```setXIsNext``` とする
- ```handleClick()``` 関数の変更
    - ```xIsNext``` 変数で手番を制御する
        - ```xIsNext``` 変数が真のときに、「X」を表示する
        - ```xIsNext``` 変数が真ではないときに、「O」を表示する
    - マスの表示を更新した後に手番を相手に譲る
        - set関数で ```xIsNext``` の真偽値を反転させる

```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);

  function handleClick(i) {
    const nextSquares = squares.slice();

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

  return (
    <>
      <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>
    </>
  );
}
```

実行結果を次に示します。これで、マスをクリックすると、「X」と「O」が交互に表示されるようになりました。

![OとXの交互.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2844961/56b2b21b-e1d1-60db-4bdc-4c39034751ea.gif)


## 表示処理の修正
現状のプログラムでは、マスの状態が空白である否かを確認せずに「X」と「O」を表示させています。従って、既にどちらかの印をつけたマスを、再びクリックすると内容を塗り替えることできるバグがあります。

![表示処理の修正前.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2844961/cbd96749-5813-d9a3-1c9d-f88950865390.gif)

これを修正するために、```Board``` コンポーネントの ```handleClick()``` 関数にて、クリックしたマスに印が既につけられている場合は、早期リターンをする処理を加えましょう。
```jsx:handleClick() 関数の変更
  function handleClick(i) {
    if (squares[i]) {
      return;
    }

    const nextSquares = squares.slice();

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

これで、既に印があるマスに対してクリックをしても、表示が変更されることはありません。

![表示処理の修正後.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2844961/28f15acc-6a25-2fa9-7c88-67f18c766a1a.gif)

# おわりに
今回は、マスで手番を実装することで、プレイヤーの概念が生まれました。次のページに現段階のソースファイルを示します。

https://codesandbox.io/p/sandbox/react-tutorial6-lfd75p

次回は、盤面の状況からゲームの勝者を判定するプログラムを開発していきます。React というよりは、どのようにアルゴリズムを導出するかを中心に記しています。

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