駆け出しエンジニアのReactチュートリアル

## 駆け出しのエンジニアです。
昨年5月に未経験からエンジニアへ転職しました。
エンジニア歴はもうすぐ1年になります。
業務ではRailsでの開発を行っています。
Javascriptは独学で少し学んだ程度の知識です。

## Reactチュートリアルに挑戦
Reactを触ってみたいと思い、チュートリアルに取り組みました。
内容は3x3の9マスで行う3目並べゲームでした。

0|1|2
-|-|-
**3**|**4**|**5**
**6**|**7**|**8**

Vue.jsの学習を行っていたこともあり、StateやPropsは馴染みやすかったです。
Javascriptの知識が少ないので、都度調べながら進めましたが「これで良いのかな」という状態でした。
引き続き知識を深めたいと思います。

https://ja.reactjs.org/tutorial/tutorial.html

## 学習記録: Github
項目毎にコミットを積んで記録を残しました。
自身で何か作るときは、今回の学習を参考にしようと思います。

https://github.com/KON-ch/react-tutorial

## 改良版にチャレンジ
チュートリアル終了後の6つのチャレンジ項目にも取り組みました。
答えは見つからなかったので、良い方法かはわかりませんが最後までやり遂げました。
気づきや考えたことを記載しています。

1. 履歴内のそれぞれの着手の位置を (col, row) というフォーマットで表示する。
https://github.com/KON-ch/react-tutorial/commit/3ca879
`i` には9マスに割り振った0~8の数字が入ります。
ここで、どの行と列に該当するか計算しています。
`(i / 3 | 0)` で小数点以下を切り捨てられることを知りました。
   ```react
   this.setState({
     history: history.concat([{
       squares: squares,
       player: squares[I],
       row: (i / 3 | 0) + 1,
       col: i % 3 + 1,
     }]),
     ...
   ```

1. 着手履歴のリスト中で現在選択されているアイテムを太字にする。
https://github.com/KON-ch/react-tutorial/commit/fa0090
`move` は着手履歴を戻すボタンを作成しているループの引数で0~9が入ります。
stateに保持している`stetNumber`(現在の着手回数)と比較しています。
`'fontWeight': 'bold'` でstyleが設定できることを知りました。

   ```react
   const style = (this.state.stepNumber === move) ? { 'fontWeight': 'bold' } : {}

   return (
     <li key={move} style={style}>
   ```

1. Board でマス目を並べる部分を、ハードコーディングではなく 2 つのループを使用するように書き換える。
https://github.com/KON-ch/react-tutorial/commit/e9053a
正直これはどうすべきだったか、わかっていません。
呼び出しは`{ this.renderSquare() }`だけになりましたが、そもそもの意図にあっていたか...
一応、マスの数の増減には対応できるようにしています。(勝敗の判定部分はそのままなのでマス数が変わるだけです。)

   ```react
   // 1つ目のループで行を指定
   const rows = 3

   // [0, 1, 2]
   const rowsArray = Array(rows).fill(null).map((_, i) => i)

   return rowsArray.map((row) => {
     return (
       <div className="board-row" key={row}>
         { this.renderRow(row) }
       </div>
     );
   });

   // 2つ目のループで列を指定
   renderRow(row) {
     const cols = 3

     // [0, 1, 2] or [3, 4, 6] or [7, 8, 9]
     const colsArray = Array(cols).fill(null).map((_, i) => row * cols + i)

     return colsArray.map((col) => {
       return this.renderCol(col);
     });
   }
   ```

1. 着手履歴のリストを昇順・降順いずれでも並べかえられるよう、トグルボタンを追加する。
https://github.com/KON-ch/react-tutorial/commit/aff233
stateに設定した `reverseSort: false` から判定しています。
ボタンではstateの値を反転させています。
`onClick={() => this.toggleSort()}` ここは関数にしないと無限ループになってボタンが切り替わり続けました。

   ```react
   // トグル用のメソッド//
   toggleSort() {
     this.setState({
       reverseSort: !this.state.reverseSort
     });
   }

   // ボタンテキスト //
   const sort = this.state.reverseSort ? 'desc' : 'asc'

   // 表示 //
   <button onClick={() => this.toggleSort()}>{sort}</button>
   <ol reversed={this.state.reverseSort}>
     {this.state.reverseSort ? moves.reverse() : moves}
   </ol>
   ```

1. どちらかが勝利した際に、勝利につながった 3 つのマス目をハイライトする。
https://github.com/KON-ch/react-tutorial/commit/bcf341
勝者の判定を行っている箇所から、勝利したマス`lines[i]`も返すようにしました。
勝利の判定ではないときも、同様のオブジェクトを返す必要がありました。
各箇所でpropsで受け取って処理を行うようにしました。

   ```react
   function calculateWinner(squares) {
   ...

     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 { winner: squares[a], line: lines[i] };
       }
     }
     return { winner: null, line: [] };
   }
   ```

1. どちらも勝利しなかった場合、結果が引き分けになったというメッセージを表示する。
https://github.com/KON-ch/react-tutorial/commit/1eed7e
手番と勝者を出力していた箇所に引き分けとなる条件を追加しました。
条件は、着手回数とマスの総数が一致した場合は引き分けとしています。

```react
if (winner) {
  status = 'Winner: ' + winner;
} else if (this.state.stepNumber === rows * cols) {
  status = '引き分け'
...
```

## 余談: React18になって
コンソールにエラーが出ていたので、チュートリアルとは別に修正しました。
参照: https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html#updates-to-client-rendering-apis

```diff_javascript
import React from "react";
- import ReactDOM from "react-dom";
+ import { createRoot } from "react-dom/client";

+ const container = document.getElementById('root');
+ const root = createRoot(container)

// いろいろ...

- ReactDOM.render(
-   <Game />,
-   document.getElementById('root')
- );
+ root.render(<Game />);
```

## まとめ: ひとまず完走
チャレンジ内容に一通り取り組みましたが、Reactはもちろん、Javascriptについても学びが足りないなと感じました。
引き続き学習に取り組みたいと思います。
最後まで読んでいただきありがとうございました。