Reactチュートリアルの要点まとめ

新卒研修にてeact×TyescriptでReact公式のチュートリアルに取り組んだので重要箇所メモ

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

難易度は初心者にとってはかなり高いように感じましたが、公式が出しているということもあり主要な機能を学習できます。

公式サイトは素のReactで書かれていますが、Typescriptを業務で使用しているので型指定を意識して回答は参考程度にして取り組みました。


## ①コンポーネントを作成する
・Square,Board,Gameの3階層構造
・単一責任原則を意識した構造

Reactでは、一つのコンポーネントが一つの責任だけを持つように設計するのがベストプラクティスです。
各コンポーネントの責任範囲の：

I Squareコンポーネント
```tsx
type SquareProps = {
  value: 'X' | 'O' | null;
  onClick: () => void;
};

function Square({ value, onClick }: SquareProps) {
  return (
    <button className="square" onClick={onClick}>
      {value}
    </button>
  );
}
```
責任：1マスの見た目とクリックを受け取るだけ（内部状態は持たない）
ロジック（状態の更新など）は親コンポーネントに委ねる

II Board コンポーネント
```tsx
type BoardProps = {
  squares: ('X' | 'O' | null)[];
  onClick: (i: number) => void;
};

function Board({ squares, onClick }: BoardProps) {
  const renderSquare = (i: number) => (
    <Square value={squares[i]} onClick={() => onClick(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>
  );
}
```
責任：9個のマスを並べて、一つの盤面として描画する
Squareの表示内容やクリック処理は親のpropsで渡す


III Gameコンポーネント
```tsx
function Game() {
  //履歴
  const [history, setHistory] = useState<[('X' | 'O' | null)[]]>([Array(9).fill(null)]);
  //誰のターンかどうか
  const [currentMove, setCurrentMove] = useState(0);
  //勝敗
  const currentSquares = history[currentMove];

  const handlePlay = (nextSquares: ('X' | 'O' | null)[]) => {
    const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];
    setHistory(nextHistory);
    setCurrentMove(nextHistory.length - 1);
  };

  const handleClick = (i: number) => {
    // 勝敗や既存マスチェック後に更新
  };

  return (
    <div className="game">
      <Board squares={currentSquares} onClick={handleClick} />
      {/* 他にも履歴の表示など */}
    </div>
  );
}
```
責任：アプリ全体の状態管理（履歴、ターン、勝敗）、コンポーネントの組み合わせ
ロジックと状態の中枢を担う。UIの構造にはあまり深入りしない。

### ポイントまとめ
・小さいコンポーネントほど、責任も小さく単純に保つ
・状態はできるだけ上位で管理し、下位のUI部品に渡す
・UIはpropsから作られる→状態が変われば再描画される

## ②propsを使用してデータを渡す
親（Board）から子（Square）に渡すためにpropsを使用する
onClickイベントをprops経由で渡すことで子が親の関数を呼び出すことができる

## ③state を使って状態を持つ
・Boardにsuaresという状態を定義する
・マスをクリックしたら値（'×'や'◯'）が状態として更新される

## ④stateをリフトアップ
Reactでは、複数のコンポーネント間で共通の状態を使用したい時、その状態を共通の親コンポーネントに移動する。
これによって状態が一元管理されることで、状態に依存する複数の子コンポーネントが同期して動作する。
Boardはprops経由で現在のsquaresとonClickを受け取るだけ
Gameが中心的な状態管理を担い、履歴管理が可能に

※補足：リフトアップが多くなると
状態をどんどん親に集めると、状態が膨れ上がって煩雑になってしまうこともある
その場合は以下のような技術で管理しやすくなる
Context API:グローバルに状態を共有する仕組み
Redux：状態管理ライブラリ

## ⑤イミュータブルなデータ構造
状態を直接変更せず、変更された新しいデータを作るという考え方

Reactは状態の変更を検知してコンポーネントを再レンダリングします。
ただし、Reactはオブジェクトの中身の変更までは自動で検出できません
そのため、状態は直接変更せず、新しいオブジェクトを作ることが必要になる

```tsx
const nextSquares = currentSquares.slice();
nextSquares[i] = 'X';
setSquares(nextSquares); 
```

イミュータブルにする主な利点
・再描画の正確な検知（確実に差分を検知することができる）
・履歴の実装（過去の状態をそのまま残せるため、履歴の保存が簡単）
・デバックや予測がしやすい（状態の変化が明確になる）

⭐️Reactでの状態管理は「変更」ではなく「新しい状態」を作ることが基本
⭐️slice,map,filter,スプレッド構文を使用してイミュータブルに保つ

## ⑥履歴機能の実装
各ターンの盤面（squares）を保存して、時間を巻き戻す「タイムトラベル」を可能にする

・各手番ごとに状態（盤面の配置）を記録する
・過去の状態に戻って盤面を復元することができる
・ゲームの経過を確認したりリプレイしたりする機能

## ⑦勝敗の判定
ゲームが勝利で終了したかどうかを判定する関数として、calculateWinner() が登場します。

・現在の盤面を見て、勝者がいるかどうかを判定する
・勝利条件に一致するマスがあるかどうかを調べる
・勝者がいれば'X'または'◯'を返し、いなければnull

```tsx
function calculateWinner(squares: Array<'X' | 'O' | null>): 'X' | 'O' | null {
  const lines = [
    [0, 1, 2], // 横1列目
    [3, 4, 5], // 横2列目
    [6, 7, 8], // 横3列目
    [0, 3, 6], // 縦1列目
    [1, 4, 7], // 縦2列目
    [2, 5, 8], // 縦3列目
    [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]; // 勝者 'X' または 'O'
    }
  }
  return null; // 勝者なし（ゲーム続行中 or 引き分け）
}


```
判定の仕組み
・linesには勝利パターンのインデックスの組み合わせを定義している
・各lines[i]のabcをチェック
　・3マスが全て同じプレイヤーかつnullではない→勝利