React チュートリアル：三目並べを TypeScript で書いてみた

# [チュートリアル： 三目並べ](https://ja.react.dev/learn/tutorial-tic-tac-toe)
サンプルコードが JavaScript なので TypeScript で書きながら勉強してみた。

# 概要
## スタートコードの確認
```typescript
export default function Square() {
  return <button className="square">X</button>
}
```
> default キーワードは、このコードを使用する他のファイルに、これがこのファイルのメイン関数であるということを伝えます。

ブルーベリー本にも
> モジュールからエクスポートされる代表的な値を表すために default という名前が使用され、それを扱うために専用の構文が用意されていると理解しましょう。

って書いてある。

そしてブルーベリー本のコラムには `default` キーワードを使うことをおすすめしてない印象があります。
理由は、
> エディタによるサポートの弱さ

を挙げています。
おすすめは、
> エクスポートする変数名を決めるときはそのままインポートできる名前にすべきである

とのことです。
正直エディタによるサポートの弱さ(自動補完されない)をまだ感じたことはないけど、「エクスポートする変数名を決めるときはそのままインポートできる名前にすべき」はそりゃそうだなって思います。`default` キーワードを使うとインポート側で変数名を自由に変えることができるから、もしインポート側で変えちゃったら可読性は落ちるよね〜。と思いつつ、インポート側で変数名を変えないことを前提に、`default` キーワードを目印代わりに使うのは悪くなさそうだな〜というのが、今の考えです。


あぁ・・・。ここ React じゃない話だ・・・。

## 盤面の作成
### この章の最終コード
[github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/「盤面の作成」が完了したコード](https://github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/blob/ddb122315127fd9ac24be0dc0a03c4609b0a9c4f/src/App.tsx)


## props を通してデータを渡す

props を渡すところで型を指定しないと、怒られます・・・
`TS7031: Binding element value implicitly has an any type.`

```diff_typescript
- function Square({ value }) {
+ function Square({ value }: { value: string }) {
    return <button className="square">{ value }</button>;
  }
```

### この章の最終コード
[github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/「props を通してデータを渡す」の最終コード](https://github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/blob/319ce56e2dea9d1081de0e9cb9f89392d94d5e43/src/App.tsx)

## インタラクティブなコンポーネントの作成
state が持つ値の型を書く。
初期値は null なのに、set する値が string だから怒られます・・・
`TS2345: Argument of type "X" is not assignable to parameter of type SetStateAction<null>`

```diff_typescript
  function Square() {
-   const [value, setValue] = useState(null)
+   const [value, setValue] = useState<string | null>(null)

    function handleClick() {
      setValue('X')
    }

    return (...)
  }
```

### この章の最終コード
[github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/「インタラクティブなコンポーネントの作成」の最終コード](https://github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/blob/850bcf1eaa7d1c181940bcb7ab9e701a4fa2902e/src/App.tsx)


## React Developer Tools
Chrome にも拡張機能があって、入れてはみたもののあんまり活用できてない感が否めない・・・。


# ゲームを完成させる
## state のリフトアップ
state を配列で持つ場合の書き方。
`[]` の書く位置を間違えたり、
`string | null` を `()` で囲うのを忘れたりして、書き方違うぜ〜って赤波線出てイライラしちゃうんだよね。

```diff_typescript
  export default function Board() {
-   const [squares, setSquares] = useState(Array(9).fill(null))
+   const [squares, setSquares] = useState<(string | null)[]>(Array(9).fill(null))

    return (...)
  }
```

チュートリアルを読み進めていると、state が持つ値は
`X` か `O` か `null` しかないことが分かる。
毎回 `string | null` と書くのはちょっと面倒だし、`string` だと `X` と `O` 以外の文字も許容していることになるので、この機会に型を宣言してそれを使い回すのがよさそう。

```diff_typescript
  import { useState } from "react";

+ type SquareValue = "X" | "O" | null;

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

  export default function Board() {
-   const [squares, setSquares] = useState<(string | null)[]>(Array(9).fill(null))
+   const [squares, setSquares] = useState<SquareValue[]>(Array(9).fill(null));

    return (...);
  }
```
useState の宣言で `(Array(9).fill(null))` こんなふうに初期化するところは初めて見たかも！
配列の中身の個数が決まってるときに使うのかな。
<br>

`Square` が取る props が複数になったので、interface で型を宣言したのと、
`handleClick` が引数を取るので、引数の型を宣言した。

```diff_typescript
- import { useState } from "react";
+ import React, { useState } from "react";

  type SquareValue = "X" | "O" | null;

+ interface SquareProps {
+   value: SquareValue;
+   onSquareClick: React.MouseEventHandler<HTMLButtonElement>;
+ }

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

  export default function Board() {
    const [squares, setSquares] = useState<SquareValue[]>(Array(9).fill(null));

+   function handleClick(i: number) {
+     const nextSquares = squares.slice();
+     nextSquares[i] = "X";
+     setSquares(nextSquares);
+   }

    return (...);
  }
```

個人的にオブジェクト型を宣言するときは、interface で宣言しています。
なぜかというと、interface はオブジェクト型しか宣言できないので、interface と書いてあれば「あ〜オブジェクト型なのね」と、すぐに理解できるからです。
他にも、型エイリアス宣言をすると `.d.ts` ファイルが大きくなり、パフォーマンスの問題もあるとか・・・。
詳しい話は、以下のリンクから読めます☆
- [インターフェースと型エイリアスの使い分け(サバイバル TypeScript)](https://typescriptbook.jp/reference/object-oriented/interface/interface-vs-type-alias#%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9%E3%81%A8%E5%9E%8B%E3%82%A8%E3%82%A4%E3%83%AA%E3%82%A2%E3%82%B9%E3%81%AE%E4%BD%BF%E3%81%84%E5%88%86%E3%81%91)
- [型リテラルのエイリアスよりもインターフェイスを優先する(Google TypeScript Style Guide)](https://google.github.io/styleguide/tsguide.html#prefer-interfaces)
- [TypeScript: インターフェイスを優先する(Nicholas Jamieson’s personal blog.)](https://ncjamieson.com/prefer-interfaces/)
- [TypeScript の大規模導入から得た 10 の洞察(Bloomberg)](https://www.bloomberg.com/company/stories/10-insights-adopting-typescript-at-scale/)<br>

ですが、interface 派と type 派がいること、それぞれの思いを持って使い分けていることも分かるので、個人的に interface ってだけで、どっちでもいいかな〜って思います(笑)
<br>


> React では、イベントを表す props には onSomething という名前を使い、それらのイベントを処理するハンドラ関数の定義には handleSomething という名前を使うことが一般的です。

そうだったんだ・・・。
何の値を props として受け取っているのか、何のための関数なのか、こうした慣習に従うことで認知的負荷を下げられるなら、活用してもよさそう。

### この章の最終コード
[github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/「state のリフトアップ」の最終コード](https://github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/blob/1c46292ecf86bca888ba352d67e3691366ef402d/src/App.tsx)


## なぜイミュータビリティが重要なのか
- 機能実装が容易
  - アクションの取り消しややり直しを簡単に実装できる
  - データの過去バージョンを保持し、再利用できる
- パフォーマンス向上
  - イミュータビリティにより、データが変更されたかどうかを容易に比較でき、不要な再レンダーを避けられる
  - React の memo API の利用で、再レンダーの最適化が可能

## 手番の処理
新しく useState が追加されました。
初期値として `true` が入っているため、型推論してくれるみたいです。
だけど型を書く☆

```diff_typescript
  export default function Board() {
-   const [xIsNext, setIsNext] = useState(true)
+   const [xIsNext, setIsNext] = useState<boolean>(true)
    const [squares, setSquares] = useState<SquareValue[]>(Array(9).fill(null))

    function handleClick(i: number) {...}
  }
```

### この章の最終コード
[github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/「手番の処理」の最終コード](https://github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/blob/9da3728588875d3eb4f03dc6bf15bdf2aa4311d5/src/App.tsx)


## 勝者の宣言
関数を宣言したときの引数はどんな型なのか？は書く必要があります。
あとは let で変数名だけを宣言している箇所も、あとから文字を代入してるけど、宣言時にどんな型が入るかも合わせて宣言するとよさそう。

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

    function handleClick(i: number) {...}

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

    return (...);
  }

- function calculateWinner(squares) {
+ function calculateWinner(squares: SquareValue[]) {
  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` 関数の if 文の `squares[a] && squares[a]` をどう理解していいか分からなかったけど、`null` が `falsy` であることを利用して `null` が入っていたら if 文から抜けるように作っているのね。
まだまだ JavaScript が読めないなんて｡｡｡＿|￣|○

### この章の最終コード
[github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/「勝者の宣言」の最終コード](https://github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/blob/5170ec92ce26730f7f1bf107bf213ff8120f4c89/src/App.tsx)


# タイムトラベルの追加
## 着手の履歴を保持する
- 過去の squares 配列を、history という配列に保存する
- history 配列は、最初の着手から最新の着手まで、盤面のすべての状態を表現する

## もう一度 state をリフトアップ
onPlay みたいな関数の型を定義するとき、いつもどう書くか分からなくなる・・・。まだ慣れない・・・。
関数の引数と引数の型、戻り値の型を書く。戻り値がない場合は void 。

```diff_typescript
+ interface BoardProps {
+   xIsNext: boolean;
+   squares: SquareValue[];
+   onPlay: (nextSquares: SquareValue[]) => void;
+ }

- function Board({ xIsNext, squares, onPlay }) {
+ function Board({ xIsNext, squares, onPlay }: BoardProps) {
    function handleClick(i: number) {...}

    ...

    return (...);
  }

  export default function Game() {
    const [xIsNext, setXIsNext] = useState<boolean>(true);
-   const [history, setHistory] = useState([Array(9).fill(null)]);
+   const [history, setHistory] = useState<SquareValue[][]>([Array(9).fill(null)]);
    const currentSquares = history[history.length - 1];

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

    return (
      <div className="game">
        <div className="game-board">
          <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />
        </div>
        <div className="game-info">
          <ol>{/*TODO*/}</ol>
        </div>
      </div>
    );
  }
```

Board コンポーネントに set 関数を渡さずに、set 関数を実行する関数(handlePlay)を作って渡してる。
私が何も考えずに実装すると、state と set 関数の両方を Board コンポーネントに渡しちゃうな・・・。もしそうすると props を４つも渡さないといけなくなるから、あんまりスマートじゃないのかも・・・。
state を更新するタイミングが同じならこうやって関数にまとめて、関数を渡すだけにするとスマートに書けるのね〜という学び。

### この章の最終コード
[github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/「もう一度 state をリフトアップ」の最終コード](https://github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/blob/08650eaf8c5b443af6810b55c254b3c1e49a803a/src/App.tsx)

## 過去の着手の表示
map で button などの要素を量産することができるのを知ったとき、ちょっと衝撃的だった気がする・・・。
map で回した結果を変数に入れて使ってるの新鮮！いつも変数に入れずにそのまま使ってる。どっちがいいんだろう？？

```diff_typescript
  export default function Game() {
    ...

    function handlePlay(nextSquares: SquareValue[]) {...}

    function jumpTo(nextMove) {
      // TODO
    }

    const moves = history.map((squares, move) => {
-     let description;
+     let description: string;
      if (move > 0) {
        description = "Go to move #" + move;
      } else {
        description = "Go to game start";
      }

      return (
        <li>
          <button onClick={() => jumpTo(move)}>{description}</button>
        </li>
      );
    });

    return (...);
  }
```

> Warning: Each child in a list should have a unique "key" prop. Check the render method of `Game`.

想定通りの Warning が出ました☆

### この章の最終コード
[github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/「過去の着手の表示」の最終コード](https://github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/blob/0a168273e718f09622dfa2dab2c24515b30f9ffa/src/App.tsx)


## key を選ぶ
- key は React に特別に予約されたプロパティ
- index を key として使うと、リストの項目を並べ替えたり、挿入、削除するときに問題が生じる
- key はグローバルに一意である必要はないが、コンポーネントとその兄弟間で一意である必要がある


## タイムトラベルの実装
ずっと赤い波線が出てる箇所があって気になってたんだけど、
`const moves = history.map((squares, move) =>` の `squares` って使ってないのね。
だけど、第２引数の index になる部分を使いたいから、省略できないし書いてたのか・・・。
アンダースコアにすると、`TS6133: squares is declared but its value is never read.` が消えるから、使わないけど省略できないときは `_` を使うとよさそう。

```diff_typescript
  export default function Game() {
    const [xIsNext, setXIsNext] = useState<boolean>(true);
    const [history, setHistory] = useState<SquareValue[][]>([
      Array(9).fill(null),
    ]);
-   const [currentMove, setCurrentMove] = useState(0);
+   const [currentMove, setCurrentMove] = useState<number>(0);

    const currentSquares = history[currentMove];

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

-   function jumpTo(nextMove) {
+   function jumpTo(nextMove: number) {
      setCurrentMove(nextMove);
      setXIsNext(nextMove % 2 === 0);
    }

-   const moves = history.map((squares, move) => {
+   const moves = history.map((_, move) => {
    let description: string;
    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(...);
  }
```

### この章の最終コード
[github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/「タイムトラベルの実装」の最終コード](https://github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/blob/2702c69ba952cf49db4d59b1e80ce7f9dcf4258d/src/App.tsx)


## 最後のお掃除
- state に格納するものを単純化すると
  - バグが減る
  - コードが理解しやすくなる

### この章の最終コード
[github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/「最後のお掃除」の最終コード](https://github.com/b-yuko/react-tutorial-tic-tac-toe-typescript/blob/e0f1948c2e75c091eff2e7e8706b4a2fc7d83329/src/App.tsx)


# おわりに
React のチュートリアルがあるのは知ってたけど、やったことはなかったので、色々気づきがあった。やってよかったよかった🥳
REACT を学ぶ ってところでも基礎的なことが学べそうなので、次はそっちをやってみようと思います！
git で細かくコミットを刻むとロールバックもしやすいし、コミットの数だけ達成感を味わえるので、よき🙌