React公式ドキュメントの理解（チュートリアル：三目並べ）

Reactを独学で学ぶ上で、いちばん最初に公式ドキュメントの理解から着手しようと思ったのでアウトプットも兼ねて内容をまとめました。
ここではReact公式ドキュメントの「**チュートリアル：三目並べ**」をまとめました。

# こんな人向け
- Reactを触ったことがない人
- Reactに興味があり、学ぼうとしている
- Reactの概念・仕組みを知りたい

# 筆者の状態
- html, css, javaScriptはある程度知っている
- Reactは初心者

# チュートリアル：三目並べ

### チュートリアルで作成するもの
以下、チュートリアルで作成する完成物の三目並べゲームになります。
```Apo.tsx
import { useState } from 'react';

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

function Board({ xIsNext, squares, onPlay }) {
  function handleClick(i) {
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    const nextSquares = squares.slice();
    if (xIsNext) {
      nextSquares[i] = 'X';
    } else {
      nextSquares[i] = 'O';
    }
    onPlay(nextSquares);
  }

  const winner = calculateWinner(squares);
  let status;
  if (winner) {
    status = 'Winner: ' + winner;
  } else {
    status = 'Next player: ' + (xIsNext ? 'X' : 'O');
  }

  return (
    <>
      <div className="status">{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>
    </>
  );
}

export default function Game() {
  const [history, setHistory] = useState([Array(9).fill(null)]);
  const [currentMove, setCurrentMove] = useState(0);
  const xIsNext = currentMove % 2 === 0;
  const currentSquares = history[currentMove];

  function handlePlay(nextSquares) {
    const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];
    setHistory(nextHistory);
    setCurrentMove(nextHistory.length - 1);
  }

  function jumpTo(nextMove) {
    setCurrentMove(nextMove);
  }

  const moves = history.map((squares, move) => {
    let description;
    if (move > 0) {
      description = 'Go to move #' + move;
    } else {
      description = 'Go to game start';
    }
    return (
      <li key={move}>
        <button onClick={() => jumpTo(move)}>{description}</button>
      </li>
    );
  });

  return (
    <div className="game">
      <div className="game-board">
        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />
      </div>
      <div className="game-info">
        <ol>{moves}</ol>
      </div>
    </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, b, c] = lines[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      return squares[a];
    }
  }
  return null;
}
```
チュートリアルを作成する前に、まずは上記のコードを動作させて動きを確認してみるのもよいと思います。

### チュートリアルのセットアップ
Reactの公式サイトでは、```CodeSandbox``` を使用してブラウザ上でコードを書きながら画面表示の確認もできるようになっているので、そちらの方が楽だと思う方は[React公式サイト](https://ja.react.dev/learn/tutorial-tic-tac-toe)からチュートリアル作成を行ってください。
ここではローカルにReactプロジェクトを作成して、チュートリアルを実施していきたいと思います。

■Reactプロジェクトの作成
プロジェクトを作成したいローカルディレクトリに移動して
```
npm create vite@latest
...
Project name:
tutorial

Select a framework:
React

Select a variant:
TypeScript
```
tutorialフォルダに移動して、```npm i```
```
cd tutorial

npm i
```
これでローカルにReactプロジェクトを作成することが出来ました。

index.cssを下記のようにしましょう。
```index.css
* {
  box-sizing: border-box;
}

body {
  font-family: sans-serif;
  margin: 20px;
  padding: 0;
}

h1 {
  margin-top: 0;
  font-size: 22px;
}

h2 {
  margin-top: 0;
  font-size: 20px;
}

h3 {
  margin-top: 0;
  font-size: 18px;
}

h4 {
  margin-top: 0;
  font-size: 16px;
}

h5 {
  margin-top: 0;
  font-size: 14px;
}

h6 {
  margin-top: 0;
  font-size: 12px;
}

code {
  font-size: 1.2em;
}

ul {
  padding-inline-start: 20px;
}

* {
  box-sizing: border-box;
}

body {
  font-family: sans-serif;
  margin: 20px;
  padding: 0;
}

.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;
}

.board-row:after {
  clear: both;
  content: "";
  display: table;
}

.status {
  margin-bottom: 10px;
}
.game {
  display: flex;
  flex-direction: row;
}

.game-info {
  margin-left: 20px;
}
```
App.tsxを下記のようにしましょう。
```App.tsx
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";

export default function Square() {
  return <button className="square">X</button>;
}

```
App.tsxにあるコードはコンポーネントを作成します。ReactのコンポーネントとはUIの部品を表す再利用可能なコードのことです。コンポーネントは、アプリケーションのUI要素を表示し、管理し、更新するために使用します。
App.tsxを見ると
```App.tsx
export default function Square() {  //ここ
  return <button className="square">X</button>;
}
```
```export``` というキーワードは、この関数をこのファイルの外部からアクセスできるようにします。```default``` キーワードは、このコードを使用する他のファイルに、これがこのファイルのメイン関数であるということを伝えます。
```App.tsx
export default function Square() {
  return <button className="square">X</button>; //ここ
}
```
```<button>``` はJSX要素(JSX element)と呼ばれます。JSX要素とは、何を表示したいかを記述するためのJavaScriptコードとHTMLタグの組み合わせです。```classNmae="square"``` はこのボタンのプロパティ、またはpropsと呼ばれるもので、CSSにボタンをどのようにスタイル付けするか伝えます。
```main.tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'

import App from './App.tsx'
```
1～5行目の説明をします。
- React
- Webブラウザとやり取りするためのReactライブラリ(React DOM)
- コンポーネント用のスタイル
- ```App.tsx``` で作成したコンポーネント

### 盤面の作成
App.tsxに戻ります。
現在の盤面(board)にはマス目(square)が1つしかありませんが、本来は9つ必要です。
ですがReactコンポーネントからは、JSX要素を複数隣り合わせて返すのではなく、単一のJSX要素を返す必要があります。
なので複数のJSX要素は以下のように（```<>``` および ```</>```)で囲むようにします。
```tsx
export default function Square() {
 return (
  <>
   <button className="square">X</button>
   <button className="square">X</button>
  </>
 );
}
```
こんな感じで表示されるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779353/4a433571-ecbb-449e-8e6f-33ff58bcce8c.png)

これをグリッド状に並べたいので、```<div>```を使用してグループ分けを行います。
```tsx
export default function Square() {
  return (
    <>
      <div className="board-row">
        <button className="square">1</button>
        <button className="square">2</button>
        <button className="square">3</button>
      </div>
      <div className="board-row">
        <button className="square">4</button>
        <button className="square">5</button>
        <button className="square">6</button>
      </div>
      <div className="board-row">
        <button className="square">7</button>
        <button className="square">8</button>
        <button className="square">9</button>
      </div>
    </>
  );
}
```
Web画面にはこのように表示されているはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779353/a65d0233-d6ad-42bb-9ca0-5aa395408b4c.png)

ここで```Square``` 関数の名前を```Board``` に変えましょう。
### propsを通してデータを渡す
```button``` タグのコピーではごちゃごちゃしているのでコンポーネントの再利用で実装してみましょう。
新しく ```Square``` 関数を作成します。
```
function Square() {
  return <button className="square">1</button>;
}

export default function Board() {
  // ...
}
```
次に、```Board``` コンポーネントを更新し、JSX構文を利用してこの ```Square``` コンポーネントをレンダーしましょう。
```App.tsx
// ...
export default function Board() {
  return (
    <>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
    </>
  );
}
```
:::note alert
コンポーネントは大文字で始める必要があることに注意してください。
:::
propsとして ```Board```コンポーネントから ```Square``` コンポーネントに渡して
1-9までのマス目を作成していきます。
```App.tsx
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";

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

export default function Board() {
  return (
    <>
      <div className="board-row">
        <Square value="1" />
        <Square value="2" />
        <Square value="3" />
      </div>
      <div className="board-row">
        <Square value="4" />
        <Square value="5" />
        <Square value="6" />
      </div>
      <div className="board-row">
        <Square value="7" />
        <Square value="8" />
        <Square value="9" />
      </div>
    </>
  );
}
```
### インタラクティブなコンポーネントの作成
では ```Square``` コンポーネントをクリックすると ```X``` が表示されるようにしてみましょう。
まず、```Square``` コンポーネントに、クリックされたことを「記憶」して"X"マークを表示してもらいます。何かを「記録」するために、コンポーネントはstateというのものを使用します。
Reactの ```useState``` を使用して ```Square``` の現在の値をstateに保存し、```Square``` がクリックされたときにその値を変更しましょう。
```App.tsx
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";

function Square() {
  const [value, setValue] = useState(null);

  function handleClick() {
    setValue("X");
  }

  return (
    <button className="square" onClick={handleClick}>
      {value}
    </button>
  );
}

export default function Board() {
  return (
    <>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
    </>
  );
}
```
このように表示されるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779353/c75ab12d-d288-494a-9ff0-f6f9ad94c3e2.png)


各Squareはそれぞれ独自のstateを保持しています。それぞれのSquareに格納されている ```value``` は、他のモノとは完全に独立しています。コンポーネントの ```set``` 関数を呼び出すと、Reactは自動的に内部にある子コンポーネントも更新します。

## ゲームを完成させる
ここまでの作業で、三目並べゲームの基本的な土台は完成しました。
ゲーム自体を完成さえるために"X"と"〇"を置けるようにすることと、勝ち負けのロジックを追加します。
### Stateのリフトアップ
```Board``` コンポーネントは各 ```Square``` のどの位置にマークが置かれているか知る必要があります。ですので親コンポーネント ```Board``` に共有のstateを宣言し、子コンポーネント ```Square``` にprops経由で渡します。
```App.tsx
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";

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

export default function Board() {
  const [squares, setSquares] = useState(Array(9).fill(null));
  return (
    <>
      <div className="board-row">
        <Square value={squares[0]} />
        <Square value={squares[1]} />
        <Square value={squares[2]} />
      </div>
      <div className="board-row">
        <Square value={squares[3]} />
        <Square value={squares[4]} />
        <Square value={squares[5]} />
      </div>
      <div className="board-row">
        <Square value={squares[6]} />
        <Square value={squares[7]} />
        <Square value={squares[8]} />
      </div>
    </>
  );
}
```
すでに ```Square``` から ```Board``` のstateを変更することはできないので、```Board``` コンポーネントから ```Square``` コンポーネントに関数を渡して、マス目がクリックされたときに ```Square``` にその関数を呼び出してもらうようにします。
```App.tsx
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.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));

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

```
```handleClick``` 関数は、```slice()``` 配列メソッドを使って ```squares``` 配列のコピー( ```nextSquares```)を作成します。※```slice``` は引数が０だとコピーを作成する

>次に、```handleClick``` は、```nextSquares``` 配列を更新して最初の（インデックス ```[0]``` の）マス目に ```X``` と書き込みます。
```seSquares``` 関数をコールすることで、Reactはこのコンポーネントのstateに変更があったことを知ります。これにより、```squares``` というstate変数を使用しているコンポーネント ```Board``` 、およびその子コンポーネント（盤面を構成している ```Square``` コンポーネントすべて）の再レンダーがトリガされます。

任意のマス目を変更できるようにすると
```tsx
function handleClick(i) {
    const nextSquares = squares.slice();
    nextSquares[i] = "X";
    setSquares(nextSquares);
}
```
次に、その ```i``` を ```handleClick``` に渡す必要があります。以下のように、JSX内で直接渡したくなりますが、これはうまくいきません。
```tsx
<Square value={squares[0]} onSquareClick={handleClick(0)} />
```
これがうまくいかない理由は、```handleClick(0)``` の呼び出しが、Boardコンポーネントのレンダーの一部として発生してしまうからです。```handleClick(0)``` は、```setSquares``` を呼び出してBoardコンポーネントのstateを更新するため、Boardコンポーネント全体が再レンダーされます。これにより、```handleClick(0)``` が再度実行され、無限ループに陥ります。
しかし、propsとして(```onSquareClick={handleClick}```)のように渡すと関数は実行されず呼び出されないので、無限ループにはなりません。
じゃあどうすればよいかというと、アロー関数を使います。
```tsx
export default function Board() {
  // ...
  return (
    <>
      <div className="board-row">
        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />
        // ...
  );
}
```
```App.tsx
export default function Board() {
  // ...
  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"を置けるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779353/617b0b4a-8f30-4734-860c-11b4b6eabee8.png)

>state 管理が Board コンポーネントに移動されたので、親である Board コンポーネントは子の Square コンポーネントに props を渡すことで、それらが正しく表示されるようにしています。Square をクリックすると、子である Square コンポーネントが親である Board コンポーネントに盤面の state を更新するように要求します。Board の state が変更されると、Board コンポーネントとすべての Square 子コンポーネントが自動的に再レンダーされます。すべてのマス目の state を Board コンポーネントにまとめて保持しておくことで、この後でゲームの勝者を決めることが可能になります。

### シーケンスを再確認
>ユーザが盤面の左上のマス目をクリックして X を置いた場合を例に、何が起こるのかをおさらいしましょう。

>左上のマス目をクリックすると、button が props として受け取った onClick 関数が実行されます。Square コンポーネントはその関数を Board から onSquareClick プロパティとして受け取っています。Board コンポーネントはその関数を JSX の中で直接定義しています。その関数は引数 0 で handleClick を呼び出します。
handleClick は引数 0 を使って、squares 配列の最初の要素を null から X に更新します。
Board コンポーネントの state である squares が更新されたので、Board とそのすべての子が再レンダーされます。これにより、インデックス 0 である Square コンポーネントの value プロパティが null から X に変更されます。
最終的に、クリックによって左上のマス目が空白から X に変わったという結果をユーザが目にすることになります。

### なぜイミュータビリティが重要なのか
```handleClick``` 内で既存の ```squares``` 配列を直接変更するのではなく、```.slice()``` を使ったのでしょうか？それはイミュータビリティ（不変性）という概念と
それが重要であるからです。
データを変更する方法は一般的に2つのアプローチがあります。
１つめのアプローチはデータの値を直接書き換える方法です。
```tsx
const squares = [null, null, null, null, null, null, null, null, null];
squares[0] = 'X';
// Now `squares` is ["X", null, null, null, null, null, null, null, null];
```
一方で、以下が元データを書き換えない方法です。
```tsx
const squares = [null, null, null, null, null, null, null, null, null];
const nextSquares = ['X', null, null, null, null, null, null, null, null];
// Now `squares` is unchanged, but `nextSquares` first element is 'X' rather than `null`
```
この利点は以下です。
- Reactがコンポーネントの変化を検知して再レンダーするため、無駄なレンダーを防ぎ、パフォーマンスの効率を上げるため
- 元のデータを再利用したい場合があるため

次に、"X"と"〇"を交互に置けるようにします。
```tsx
  const [xIsNext, setXIsNext] = useState(true);
  const [squares, setSquares] = useState(Array(9).fill(null));

  function handleClick(i) {
    const nextSquares = squares.slice();
    if (xIsNext) {
      nextSquares[i] = "X";
    } else {
      nextSquares[i] = "O";
    }
    setSquares(nextSquares);
    setXIsNext(!xIsNext);
  }
```
このようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779353/b0738b67-bc1d-4d47-a520-ed6243c943e3.png)
今のままでは"X"が"〇"に上書きされてしまうので
```tsx
function handleClick(i) {
  if (squares[i]) {
    return;
  }
  const nextSquares = squares.slice();
  //...
}
```
すでにマークがあれば、戻るようにします。
### 勝者の宣言
勝者がどちらかのロジックを追加します。
```Board``` は勝者が決まればゲームを終了し、勝者を表示します。ゲーム中は次のプレイヤーを表示します。
```App.tsx
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";

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

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

  function handleClick(i) {
    if (squares[i] || calculateWinner(squares)) {
      return;
    }
    const nextSquares = squares.slice();
    if (xIsNext) {
      nextSquares[i] = "X";
    } else {
      nextSquares[i] = "O";
    }
    setSquares(nextSquares);
    setXIsNext(!xIsNext);
  }
  const winner = calculateWinner(squares);
  let status;
  if (winner) {
    status = "Winner: " + winner;
  } else {
    status = "Next player: " + (xIsNext ? "X" : "O");
  }
  return (
    <>
      <div className="status">{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, b, c] = lines[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      return squares[a];
    }
  }
  return null;
}
```
次のプレイヤー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779353/1bfba578-fb68-4cf1-aa41-5244daed8abb.png)
勝ったとき
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779353/80d7bbcc-de1e-4233-bd62-2d803b672849.png)

### タイムトラベルの追加
巻き戻し要素を入れていきます。

#### 着手の履歴を保持する
過去の ```squares``` 配列を、```histroy``` という名前の別配列に入れて、それを新たにstate変数として保持することにします。```histroy``` 配列は、最初の着手から最新の着手まで、盤面のすべての状態を表現しており、以下のような形になります。
```tsx
[
  // Before first move
  [null, null, null, null, null, null, null, null, null],
  // After first move
  [null, null, null, null, 'X', null, null, null, null],
  // After second move
  [null, null, null, null, 'X', null, null, null, 'O'],
  // ...
]
```
#### もう一度stateをリフトアップ
新しいトップレベルのコンポーネント、```Game``` を作成して、過去の着手の一覧を表示するようにします。ゲームの履歴全体を保持するstateである ```history``` はここに置きます。
>history 状態を Game コンポーネントに配置することで、その子になる Board コンポーネントからは squares の state を削除できます。Square コンポーネントから Board コンポーネントに「state をリフトアップ」したときと同じように、Board からトップレベルの Game コンポーネントに state をリフトアップすることになります。これにより、Game コンポーネントは Board のデータを完全に制御し、history からの過去の盤面の状態を Board にレンダーさせることができます。

```tsx
function Board() {
  // ...
}

export default function Game() {
  return (
    <div className="game">
      <div className="game-board">
        <Board />
      </div>
      <div className="game-info">
        <ol>{/*TODO*/}</ol>
      </div>
    </div>
  );
}
```
#### 過去の着手の表示
複数の履歴を表示するためにmap関数を使います。
```App.tsx
function Board({ xIsNext, squares, onPlay }) {
  function handleClick(i) {
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    const nextSquares = squares.slice();
    if (xIsNext) {
      nextSquares[i] = "X";
    } else {
      nextSquares[i] = "O";
    }
    onPlay(nextSquares);
  }
//...
}
export default function Game() {
  const [xIsNext, setXIsNext] = useState(true);
  const [history, setHistory] = useState([Array(9).fill(null)]);
  const currentSquares = history[history.length - 1];

  function handlePlay(nextSquares) {
    setHistory([...history, nextSquares]);
    setXIsNext(!xIsNext);
  }

  function jumpTo(nextMove) {}

  const moves = history.map((squares, move) => {
    let description;
    if (move > 0) {
      description = "Go to move #" + move;
    } else {
      description = "Go to move start";
    }
    return (
      <li>
        <button onClick={() => jumpTo(move)}>{description}</button>
      </li>
    );
  });

  return (
    <div className="game">
      <div className="game-board">
        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />
      </div>
      <div className="game-info">
        <ol>{moves}</ol>
      </div>
    </div>
  );
}
```
ただ、今の状態では以下のエラーが出ます。
```
hook.js:608  Each child in a list should have a unique "key" prop.
```
#### keyを選ぶ
>リストをレンダーすると、React はレンダーされたリストの各アイテムに関するとある情報を保持します。そのリストが更新されると、React は何が変更されたのかを判断する必要があります。例えばリストのアイテムを追加したのかもしれませんし、削除、並べ替え、あるいは項目の中身の更新を行ったのかもしれません。

次のような状況から：
```jsx
<li>Alexa: 7 tasks left</li>
<li>Ben: 5 tasks left</li>
```
以下に遷移したと想像してください：
```tsx
<li>Ben: 9 tasks left</li>
<li>Claudia: 8 tasks left</li>
<li>Alexa: 5 tasks left</li>
```
>人間がこれを読めばおそらく、カウントの数字が変わっていることに加えて、Alexa と Ben の順番が入れ替わり、Claudia が Alexa と Ben の間に挿入された、と言うことでしょう。しかし、Reactはコンピュータプログラムでありあなたの意図を理解できません。

なのでReactでは以下のように記述してあげる必要があります。
```tsx
<li key={user.id}>
  {user.name}: {user.taskCount} tasks left
</li>
```
#### Reactにおけるリストと`key`プロパティ

Reactでリストをレンダリングする際、各リスト項目に`key`という特別なプロパティを指定することが重要です。

* **`key`の役割:** Reactは`key`を使って、リスト内の各項目を識別します。リストが再レンダリングされる際、Reactはこの`key`を頼りに、以前のリストと比較して、どの項目が追加・削除・移動されたかを効率的に判断します。
* **Stateの維持:** `key`が同じであれば、対応するコンポーネントは維持され、その内部状態（state）も保持されます。逆に`key`が変わると、古いコンポーネントは破棄され、新しいstateを持つ新しいコンポーネントが作成されます。
* **`key`はReact内部用:** `key`はpropsのようには見えますが、子コンポーネントに直接渡されるわけではなく、Reactが更新を判断するために内部的に使用します。
* **適切な`key`の設定:** 動的なリストでは、各項目を一意に識別できる安定した`key`（例：データのID）を設定することが強く推奨されます。適切なIDがない場合は、データ構造を見直すことも検討しましょう。
* **インデックスを`key`にするのは非推奨:** `key`を指定しない場合、Reactはエラーを出し、デフォルトで配列のインデックスを使用します。しかし、インデックスを`key`として使うと、リストの並び替えや項目の挿入・削除時に予期せぬ挙動やパフォーマンスの問題を引き起こす可能性があるため、ほとんどの場合推奨されません。
* **`key`の一意性の範囲:** `key`はリスト全体で完全に一意である必要はなく、同じリスト内の兄弟要素間で一意であれば十分です。

## タイムトラベルの実装
戻りたい時点のボタンを押下すると、履歴がその時点まで戻るようにします。
```App.tsx
import { useState } from "react";

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

function Board({ xIsNext, squares, onPlay }) {
  function handleClick(i) {
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    const nextSquares = squares.slice();
    if (xIsNext) {
      nextSquares[i] = "X";
    } else {
      nextSquares[i] = "O";
    }
    onPlay(nextSquares);
  }

  const winner = calculateWinner(squares);
  let status;
  if (winner) {
    status = "Winner: " + winner;
  } else {
    status = "Next player: " + (xIsNext ? "X" : "O");
  }

  return (
    <>
      <div className="status">{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>
    </>
  );
}

export default function Game() {
  const [history, setHistory] = useState([Array(9).fill(null)]);
  const [currentMove, setCurrentMove] = useState(0);
  const xIsNext = currentMove % 2 === 0;
  const currentSquares = history[currentMove];

  function handlePlay(nextSquares) {
    const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];
    setHistory(nextHistory);
    setCurrentMove(nextHistory.length - 1);
  }

  function jumpTo(nextMove) {
    setCurrentMove(nextMove);
  }
  
  const moves = history.map((squares, move) => {
    let description;
    if (move > 0) {
      description = "Go to move #" + move;
    } else {
      description = "Go to move start";
    }
    return (
      <li key={move}>
        <button onClick={() => jumpTo(move)}>{description}</button>
      </li>
    );
  });

  return (
    <div className="game">
      <div className="game-board">
        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />
      </div>
      <div className="game-info">
        <ol>{moves}</ol>
      </div>
    </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, b, c] = lines[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      return squares[a];
    }
  }
  return null;
}
```
お疲れ様でした。
こちらでチュートリアルは終了です。
Reactの公式を全て載せているわけではないので、あくまで自身のアウトプット用として書かせていただきました。

本記事は、Reactの公式ドキュメント（[React 公式サイトへのリンク](https://react.dev/)）を参考に、Creative Commons Attribution 4.0 International License (CC BY 4.0) の下で公開されている情報に基づいて作成されています。