普段Rails触ってる人がReactチュートリアルやってみた

# これは

- 業務でRails（Ruby）を書いている人がフロントの勉強としてReactチュートリアルをやってみたMEMOです
- 書き方悪いところやリファクタリングできるところが山ほどあると思いますが、多めに見てくださるか優しくご指摘頂けると助かります。。（間違っているところは容赦無くお願いします）

## この時のわたし

- Rails（Ruby）でバックエンドの実装に携わる
- 業務でフロント連携することが多く、自分でもさわれたらいいなーと思った
- とりあえずJS（ES6）の基礎の基礎だけ学んだ
- Rubyの他にちゃんと触ったことある言語はHTML・CSS・JS（jQuery）くらい

## リポジトリ

https://github.com/hak-chami/react_tutorial

# Reactチュートリアルとは

- Reactの公式チュートリアル
    - https://ja.reactjs.org/
- Reactだけで三目並べ（いわゆるマルバツゲーム）を作れる
- 今現在日本語版もできており大変ありがたい


公式の前提知識という項目に

- HTML と JavaScript に多少慣れていることを想定
- 関数、オブジェクト、配列、あるいは（相対的には重要ではありませんが）クラスといったプログラミングにおける概念について、馴染みがあることを想定
- また ES6 という JavaScript の最近のバージョンからいくつかの機能を使用していることにも注意

とあるので、その辺りについて軽く予習した方が効率が良いかと思います

## チュートリアルの準備

- **チュートリアルを始める前に**という項目があるので、それに沿って準備をする
- 手順や説明など詳しくチュートリアルに書いてある部分は割愛
- 詰まったところやわからなくて調べたところをメインに残している

[チュートリアル：React の導入 – React](https://ja.reactjs.org/tutorial/tutorial.html)

1. ブラウザでコードを書く
2. ローカルの開発環境で作る

どちらか選べますが、わたしは**ローカルの開発環境で作る**方で進めました

[新しい React アプリを作る – React](https://ja.reactjs.org/docs/create-a-new-react-app.html#create-react-app)

- npx?
  - npm 5.2 から利用できるパッケージランナーツール
- npm?
    - `Node Package Manager`
    - Nodeを管理するツール
    
ローカルで開発できるようにし、さらに手順に沿って進めると

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/1071da03-03af-4356-4841-dc599bf38564.png)

- 三目並べ用の枠ができた！

この状態でRailsと同じようにターミナルから

```bash
nps start
```

とすると`localhost:3001`でブラウザで表示できるようになります

- nps?
    - `npm-package-scripts`
    - https://www.npmjs.com/package/nps


## 概要

これで準備が終わったので次は概要を学んでいきます

### React とは？

- React？
  - JavaScriptのライブラリ
  - コンポーネントと呼ばれる部品を組み合わせて複雑なUIを実現できる
- `props` = `properties` の略
- `render`で表示するビューの階層（説明書き）を返す
- Reactがその説明を読み取って画面に描画する
- `Babel`は`ES6`->`ES5`のトランスパイラ

### スターターコードの中身を確認する

最初に作ったindex.jsを見てみると`React.Componentを`継承したクラスが3つありました

- `extends`で継承(ES6)
- ex) `Square`クラスが`React.Component`クラスを継承（extends）している

```jsx

class Square extends React.Component
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/7ab3042d-7b99-6af4-a74b-a9fc98e006f8.png)

- `Game`クラスはこの時点でまだ実装されていない

### データを Props 経由で渡す

チュートリアル通りにコードを書き換えていくとこんな感じで表示されるようになりました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/c4d9740c-b3f4-889b-3e44-77f2de1c5de2.png)


- React では、親から子へと`props`を渡すことで、アプリ内を情報が流れていく
- ここでは`Board`（親）-> `Square`（子）へ`props`として`value`という名前の値を`Square`に渡している

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/493f5466-0ead-6106-7b8b-e94a84368f8d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/450ff871-5ada-5088-17d7-25a397bc9952.png)


### インタラクティブなコンポーネントを作る

 > this の混乱しやすい挙動を回避するため、この例以降ではアロー関数構文をつかってイベントハンドラを記述
 
- アロー関数は親の`this`を継承する（ES6）
- `onClick`プロパティに渡しているのは関数なのでReactはクリックされるまでこの関数を実行しない

これでクリックされた時のイベントを作れたので今度はクリックされたら`X`が表示されるようにします

- Reactコンポーネントはコンストラクタで`this.state`を設定することで、状態を持つことができるようになる
- 現在の`Square`の状態を`this.state`に保存して、マス目がクリックされた時にそれを変更することができる

`Square`の`constructer`内で`this.state`として`value: null`を定義し、
`render`する時に渡された`props`ではなく`this.state`とすることで初期値の`value: null`が反映され↓の状態に戻りました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/393a0aff-c65a-c439-c40f-0796393acfac.png)


クリックイベントで`alert`を表示していたところを

```js
() => this.setState({ value: 'X' })
```

とすることで`state`の値を`X`に更新して表示できるようになりました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/80c1fd81-ebc4-ec37-ed44-0a6a3de58c64.png)


- `Square`の`render`メソッド内に書かれた`onClick`ハンドラ内で`this.setState`を呼び出すことで、Reactに `<button>` がクリックされたら常に再レンダーするよう伝えているため
- `setState`をコンポーネント内で呼び出すと、React はその内部の子コンポーネントも自動的に更新する

### Developer Tools

- ChromeにReactのDeveloperツールを入れよう
- 下記リンクからインストールできる
  
[React Developer Tools - Chrome ウェブストア](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=ja)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/dd08d2b8-f089-90bd-5c9e-0f529fb041d0.png)


## ゲームを完成させる

ここからゲームにしていくために下記を実装してきます

> 完全に動作するゲームにするためには、盤面に “X” と “O” を交互に置けるようにすることと、どちらのプレーヤが勝利したか判定できるようにすることが必要

### State のリフトアップ

- 親コンポーネント内で共通の`state`を宣言することで子コンポーネント同士を兄弟のようにでき、互いにやりとりできるようになる
- 親コンポーネントは`props`を使って子に情報を返せる（ここでは`value={i};`の部分）
    - こうすることで子コンポーネント同士、親子同士で常に同期されるようになる

```jsx

this.state = {
      squares: Array(9).fill(null),
    };

// Array(9) -> 9この要素を持った配列を作る, fill(null) -> 配列の要素を全てnullで埋める
```

[【JavaScript】配列を同じ値で埋めたり、連続した値を設定する - Qiita](https://qiita.com/Nossa/items/e420c15175d87cec079e)

こうすると`Board`の`state`として

```jsx
[
  X, O, null,
  X, null, O,
  null, X, null
]
```
みたいな配列を持つことになります

- ここを更新してその値を子に伝えることで子の`state`を管理することができる
- 子がクリックされた時に親の`state`を変更したいが、できないので親の関数を呼んで`state`を更新するようにする

これで子が自身の`state`を管理しなくなったので子の`constructor`はいらなくなりました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/c5acdb28-94f7-3b2e-c120-f354b6270e32.png)


`handleClick()`を定義していないと怒られたので定義し、クリックされたら`squares`の値を更新してやるとまた`X`が表示されるように！

```jsx
handleClick(i) {
  const squares = this.state.squares.slice();
  squares[i] = 'X';
  this.setState({squares: squares});
}

// arr.slice(begin[, end]); -> beginを省略するとindex0からになる
```

[.slice() | JavaScript 日本語リファレンス | js STUDIO](http://js.studio-kingdom.com/javascript/array/slice)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/9abc1722-2871-b3ca-f88c-beeceef1df46.png)


### イミュータビリティは何故重要なのか

- イミュータブルなオブジェクトは変更が簡単
- 参照しているイミュータブルなオブジェクトが前と別のものであれば、変更があったということ
- Reactにおいてはコンポーネントをいつ再`render`すべきなのか決定しやすくなる

###  関数コンポーネント

- 関数コンポーネント
  - `render`メソッドだけを有して自分の`state`を持たないコンポーネントを、よりシンプルに書くための方法

`Square`クラスは自分で`state`の管理をしなくなって`constructor`を削除したので関数コンポーネントに置き換えられます

### 手番の処理

```jsx
 squares[i] = this.state.xIsNext ? 'X' : 'O';
```

- 三項演算子
    - [条件 (三項) 演算子 - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Conditional_Operator)

`this.state.xIsNext`は`true`or`false`で値を持っており、クリックイベントで`state`の真偽値を反転させます
そして`true`ならXを`false`ならOに`squares[i]` を更新するようにします

```jsx
constructor(props) {
  super(props);
  this.state = {
    squares: Array(9).fill(null),
    xIsNext: true, // ここで次の手がOかXかの状態を管理
  };
}

handleClick(i) {
  const squares = this.state.squares.slice();
  squares[i] = this.state.xIsNext ? 'X' : 'O';
  this.setState({
    squares: squares,
    xIsNext: !this.state.xIsNext, // クリックしたら真偽値を反転させてstateを更新する
  });
}
```

### ゲーム勝者の判定

勝ち負け判定ができる関数が用意されているのでそれを使用します

- 勝ったプレイヤーがいたら`X`か`O`を、いなかったら`null`が返る

```jsx

render() {
  const winner = calculateWinner(this.state.squares);
  let status;
  // winnerがnullじゃなかったらどちらかが勝ちで X or O が返ってきている
  if (winner) {
    status = 'Winner: ' + winner;
  } else {
    status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
  }
```

また、`handleClick(i)`内に下記を追加することで「決着がついている」「すでに盤面が埋まっている」場合、クリックで`X`or`O`の更新をしないようになっています

```jsx

if (calculateWinner(squares) || squares[i]) {
    return;
  }
```

最初、なんで`squares[i]`？って思ってましたが、すでに`X`が入っているところが`O`に更新されたらおかしいですもんね
よく考えたら当たり前のことでした…

## タイムトラベル機能の追加

ゲームの進行状況を巻き戻したり、盤面をリセットしたりする機能を追加していきます

### 着手の履歴の保存

`squares`の配列を着手があるたび`history`という名前の別の配列に保存していくことで履歴として保持することができます

### State のリフトアップ、再び

Gameクラスで全ての値を管理します

クリックイベントは
- `square`でどの盤面がクリックされたか判断する
- `Board`からクリックされた盤面の情報を`Game`に伝える
- `GameのhandleClick`で値を更新する
- `handleClick`で盤面の情報を`history`に追加して記録していく

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/411239c6-f1d2-f220-0140-4754594b6a7f.png)


### 過去の着手の表示

```jsx

// mapの第二引数はindex(何回目か)
const moves = history.map((step, move) => {
  const desc = move ?
    'Go to move #' + move :
    'Go to game start';
  return (
    <li>
      <button onClick={() => this.jumpTo(move)}>{desc}</button>
    </li>
  );
});
```
[Array.prototype.map() - JavaScript | MDN - Mozilla](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map)

### key を選ぶ

リストを`render`する際Reactは`key`で差分を測る（`key` = Reactの予約語）

```html
<li key=重複しない値>リストの中身</li>
<!-- コンポーネントとその兄弟の間で一意であればOK（グローバルで一意の必要はない） -->
```

- もし以前になかった`key`がリストに含まれていれば、Reactはコンポーネントを作成する
- もし以前のリストにあった`key`が新しいリストに含まれていなければ、Reactは以前のコンポーネントを破棄する
- もし2つの`key`がマッチした場合、対応するコンポーネントは移動する
- `key`はそれぞれのコンポーネントの同一性に関する情報をReactに与え、それによりReactは再レンダー間で`state` を保持できるようになる
- もしコンポーネントの`key`が変化していれば、コンポーネントは破棄されて新しい`state`で再作成される
- コンポーネントが自身の`key`について確認する方法はない

以上のことから**動的なリストを構築する場合は正しい`key`を割り当てることが強く推奨される**


```jsx

const history = this.state.history.slice(0, this.state.stepNumber + 1);
// array.slice(begin, end); -> 第二引数は抜き出す要素の終わりを指定する
```

これにより任意の範囲でhistoryを取り出す（まきもどした時点以降の履歴を削除する）

[Array.prototype.slice() - JavaScript - MDN - Mozilla](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/slice)

# 拡張課題

ここからが本題！ということでそれぞれ考えながら実装してみました
拡張課題は全部で6つあります

1. 履歴内のそれぞれの着手の位置を (col, row) というフォーマットで表示する。
2. 着手履歴のリスト中で現在選択されているアイテムをボールドにする。
3. Board でマス目を並べる部分を、ハードコーディングではなく 2 つのループを使用するように書き換える。
4. 着手履歴のリストを昇順・降順いずれでも並べかえられるよう、トグルボタンを追加する。
5. どちらかが勝利した際に、勝利につながった 3 つのマス目をハイライトする。
6. どちらも勝利しなかった場合、結果が引き分けになったというメッセージを表示する。

## 1. 履歴内のそれぞれの着手の位置を (col, row) というフォーマットで表示する。

着手した盤面のcol,rowを一緒に保存したいというとで、

```
Go to move #1（col: 1, row: 3）
```

みたいな感じで表示したいなーと考えました

まずどうやってcolとrowの出すかだけど、ここがめっちゃ悩んだ気がします。。
図を書いて考えて

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/50fcc6b1-b12a-657b-0826-1e24395f2169.png)


```jsx

// col
i % 3 + 1;
//row
i / 3（の商） + 1;
```
とりあえず、これで
ということで、rowは商の部分だけ欲しいので整数にするメソッドを探した

[実数を整数に丸める4パターン（JavaScript おれおれ Advent Calendar 2011 – 7日目） | Ginpen.com](https://ginpen.com/2011/12/07/rounding/)

> 与えられた値を上回らない最大の整数、つまり同じか小さい整数を返します。

```js
//row
Math.floor(1 / 3) + 1;
```

次にコードのどこに書くか、ですが
表示するところは`move`のところなので、コードを見てみると
`Game`内の`render`部分で`histroy`を`map`して繰り返し表示しているから、`history`に`squares`と共に持たせるのが良さそうと思い
まず`history`の中に`col`と`row`の情報を持たせました

```jsx

this.setState({
  history: history.concat([{
    squares: squares,
    // ここにさっき出した計算式を入れた
    col: (i % 3) + 1,
    row: Math.floor(i / 3) + 1,
  }]),
  stepNumber: history.length,
  xIsNext: !this.state.xIsNext,
});
```

で、`render`内では`map`で順番に取り出した`step`のなかの`col`と`row`なので一緒に表示させてあげます

```jsx

const moves = history.map((step, move) => {
  const decs = move ?
    // historyで持ってるcolとrowの値を表示する
    `Go to move #${move}（col: ${step.col}, row: ${step.row}）`:
    `Go to start`;
  return (
    <li key={move}>
      <button onClick={() => this.jumpTo(move)}>{decs}</button>
    </li>
  );
});
```
文字列はテンプレートリテラルを使って書き換えてます！（かきやすい！）
これで、できました！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/94813e67-bdb3-32c2-40ee-b270fb11eeab.png)

## 2.着手履歴のリスト中で現在選択されているアイテムをボールドにする。

着手履歴を表示しているところは上記と同じく`Game`内の`render`部分なので、条件で表示を変えるif文を使えば良さそうです

- JSXでのif文の表示はこちらを参考
    - [条件付きレンダー – React](https://ja.reactjs.org/docs/conditional-rendering.html#inline-if-with-logical--operator)

「現在選択されているアイテム=最新の履歴」と考えて、

- もし`move`が`history`の中身の数だったら
  - 文字をボールドにして表示する
  - そうじゃなかったら普通に表示する

とすれば良さそう！

`history.length`は`state`の中の`stepNumber`で定義されているのでそれを使えそうなので、`return`の中身を

```jsx

return (
  <li key={move}>
    <button onClick={() => this.jumpTo(move)}>
      { move === this.state.stepNumber ? <strong>{decs}</strong> : decs }
    </button>
  </li>
);
```

こんな感じで書き換えました！
条件分岐の処理は複雑じゃないのでインラインで三項演算子を使っています

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/2f032e11-995b-a99b-342c-7295934ab53d.png)


## 3.Board でマス目を並べる部分を、ハードコーディングではなく 2 つのループを使用するように書き換える。

**Board でマス目を並べる部分**は繰り返しが2回あります

- 1つは`<div></div>`を3回繰り返すところ
- もう一つはそのなかで`{this.renderSquare(i)}`を3回繰り返すところ

`map`をネストすればやりたいことができそう
また、チュートリアルの最後に書いてあったように

**動的なリストを構築する場合は正しい key を割り当てることが強く推奨される**

なのでそれぞれに`key`を渡す必要がありそうです
`history`表示させているとこも`map`の`index`を`key`にしているので、こちらも同じようにしてみます

```jsx

render() {
  const boardSquares = [[0, 1, 2], [3, 4, 5], [6, 7, 8]];
  return (
    <div>
      { 
        boardSquares.map((rowSquares, i) => {
          return(
            <div className="board-row" key={i}>
              {
                rowSquares.map((square) =>{
                  return(
                    this.renderSquare(square)
                  )
                })
              }
            </div>
          )
        })
      }
    </div>
  );
  }
}
```

これでうまく表示されたけど、ちょっとパワープレイ感あるのでどうにかリファクタしたいですね。。
あと`renderSquare`の部分にも`key`持たせなかったら怒られてたので追加

```jsx

renderSquare(i) {
  return <Square
            value={ this.props.squares[i] }
            onClick={ () => this.props.onClick(i) }
            key={i}
          />;
}
```
`boardSquares`の部分をいい感じに配列作れるようにできれば…？と思ってちょっとチャレンジしてみようと思い
どうやったらいいかちょっと考えました

- まず、盤面の一辺のマス目を決める
    - 3
- 正方形の盤面なので、マス目の数は一辺の2乗
    - 9
- マス目の数を0から始めてマス目分だけ順番に配列にする
    - [0, 1, 2, 3, 4, 5, 6, 7, 8]
- その配列を一辺の数ごとに配列でくくる
    - [[0, 1, 2], [3, 4, 5], [6, 7, 8]]

そしてできたのがこちら
ちょっと長くなったけど、`boardLines`の数を変えると盤面がいい感じに変わるようになりました！

```jsx

render() {
  // 一辺が何マスの盤面を作るか
  const boardSideLength = 3;
  // boardSideLengthから盤面のsquareの数を出して0から始まる配列にする
  const boardSquares = [...Array(boardSideLength ** 2).keys()];
  // 配列からrowごとのまとまりを作る
  const boardSquaresList = boardSquares.reduce((square, i) => {
    const lastSquare = square[square.length - 1];
    if (lastSquare.length === boardSideLength) {
      square.push([i]);
      return square;
    }
    lastSquare.push(i);
    return square;
  }, [[]]);
  return (
    <div>
      { 
        boardSquaresList.map((rowSquares, i) => {
          return(
            <div className="board-row" key={i}>
              {
                rowSquares.map((square) =>{
                  return(
                    this.renderSquare(square)
                  )
                })
              }
            </div>
          )
        })
      }
    </div>
  );
  }
}
```

- `Range`で配列にするっぽいのは下記を参考に
- スプレッド演算子で`boardSideLength ** 2`の数だけ展開して`Object.keys()`でindexを取得しているのかな？わかりやすくて良かったです
    - [jsでrange関数をつくる](https://qiita.com/akameco/items/a2b698dd4a067754997b)
    - [Object.keys() - JavaScript | MDN - Mozilla](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/keys)

（Rubyで書くとこんなかなぁと思いつつ）

```rb
board_squares = Array(0..(board_side_length ** 2)-1)
```

- そこから配列を加工するのは下記を参考に
- `reduce`の使い方まだ慣れないなーと思いつつなるほどなーと思った
- 初期値を`[[]]`にして、`lastSquare.length === boardSideLength`になったらまた新しい配列に追加する、とか全然考えつかなかった、、
    - [配列をn個ずつの配列に分割してそれをまとめた配列を作る](http://fukuchiharuki.me/wiki/index.php?JavaScript/%E9%85%8D%E5%88%97%E3%82%92n%E5%80%8B%E3%81%9A%E3%81%A4%E3%81%AE%E9%85%8D%E5%88%97%E3%81%AB%E5%88%86%E5%89%B2%E3%81%97%E3%81%A6%E3%81%9D%E3%82%8C%E3%82%92%E3%81%BE%E3%81%A8%E3%82%81%E3%81%9F%E9%85%8D%E5%88%97%E3%82%92%E4%BD%9C%E3%82%8B)

そんなわけで盤面を4×4にしたり

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/46225fc6-7d39-c9bf-40cd-86501109cf86.png)


10×10にしてもちゃんと`render`されるようになりました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/2fdf41dd-cc36-5776-7724-01decc1bc367.png)

（なお当然だがゲームとしては成り立っていないw）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/e0024e5f-4a38-6c1f-ffe2-e64381da6f6c.png)

勝ち負け判定とcol,rowの計算で3って置いてるのどうにか直したら五目並べ的なゲームにできそう感はあるけど難しそうですね（勝ち負け判定のところが特に）

## 4.着手履歴のリストを昇順・降順いずれでも並べかえられるよう、トグルボタンを追加する。

だんだん考えることが多くなってきたので、順を追いながら一つずつ解決してみることにしました

- とりあえずボタンを作成したい
- どこに？
    - `Game`の表示部分、`status`の下あたり

```jsx

const orderBottun = `ASC <-> DES`;  // buttonの中身

return (
  <div className="game">
    <div className="game-board">
      <Board
        squares={current.squares}
        onClick={(i) => this.handleClick(i)}
      />

    </div>
    <div className="game-info">
      <div>{status}</div>
      <div><button>{orderBottun}</button></div> // buttonを追加
      <ol>{moves}</ol>
    </div>
  </div>
);
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/82818e45-64af-88ca-cba2-f3bdc3fb96db.png)

- ステータスに昇順か降順かの値を持たせる(`true`/`false`で切り替え？)
    - `isAsc` = `true`(昇順)/`false`(降順)
- どこで？
    - `Game`の`state`
- 降順での表示方法は？
    - `moves`を逆にすれば良さそう
        - [Array.prototype.reverse() - JavaScript | MDN - Mozilla](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse)
    - `isAsc`が`false`だったら`moves.reverse()`にする！

```jsx

constructor(props) {
  super(props);
  this.state = {
    history: [{
      squares: Array(9).fill(null),
    }],
    stepNumber: 0,
    xIsNext: true,
    // いったんfalseで置いて降順になるか確認
    isAsc: false,
  };
}
```

```jsx

return (
  <div className="game">
    <div className="game-board">
      <Board
        squares={current.squares}
        onClick={(i) => this.handleClick(i)}
      />

    </div>
    <div className="game-info">
      <div>{status}</div>
      <div><button>{orderBottun}</button></div>
      <ol>
        { this.state.isAsc ? moves : moves.reverse() } // falseなら逆で並べる
      </ol>
    </div>
  </div>
);
```

**2.着手履歴のリスト中で現在選択されているアイテムをボールドにする。** の時とやってることは同じですね

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/ea558adb-955a-d40e-2df9-cea5accc2970.png)


逆になっていますね！

- ボタンをクリックしたら`isAsc`の`true`/`false`が逆になるようにする
- まずクリックした時のイベントを作る
    - `jumpTo`を参考に押されたら`state`を更新する -> `isAsc`の`true`/`false`の入れ替え

```jsx

handleOrder() {
  this.setState({
    isAsc: !this.state.isAsc,
  });
}
```
- ボタンに`onClick`イベントを作る

```jsx

<button onClick={() => this.handleOrder()}>
  {orderBottun}
</button>
```

- ついでに今どっちの並び順なのかボタンでわかるようにしてみた

```jsx

const orderBottun = this.state.isAsc ? '↑OLD NEW↓' : '↑NEW OLD↓';
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/84c82327-ac02-abf8-9a1d-24a5d5f275d2.png) ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/c12d5195-2727-8455-5a72-313f71329953.png)



どっちが新しい手順なのかボタンでわかるようになりました


## 5.どちらかが勝利した際に、勝利につながった 3 つのマス目をハイライトする。

勝った時にどのラインで勝ったのかがわかれば、該当のマスだけ色をつける、といったことができそうです

- 勝ち判定がでたときに、一緒に該当のラインを返すようにする

```jsx

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する
    return { player: squares[a], line: [a, b, c] };
  }
}
return null;
```

- これで勝ったときに勝ちプレイヤーとラインが入った`Object`が返ってきた

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/8c1f4d22-9af2-e989-117a-40c4d1e705f3.png)

- `winner`表示のところで`Object`から`player`を取り出して表示する

```jsx

const winnerInfo = calculateWinner(current.squares);
```

```jsx

let status;
if (winnerInfo) {
  status = `Winner: ${winnerInfo.player}`; 
} else {
  status = `Next player: ${this.state.xIsNext ? 'X' : 'O'}`;
}
```

これでちゃんと表示されますね

- 次に、色をつけるところ
- `line`から勝ち判定のマスを引っ張ってきて色をつければいい
- どこに？
    - `Square`の`<button> ... </button>`
- いったんCSSにハイライトの設定を作って反映させてみる

```css
.highlight-color {
  background: #ff0000;
}
```
```jsx

function Square(props) {
  return (
    <button className="square highlight-color" onClick={props.onClick}>
      {props.value}
    </button>
  );
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/3c364074-500f-6cfb-1298-71f1537e529b.png)

できました！

- `className`には続けてCSSを指定してあげればOKだった
    - [[フロントエンド] React.jsのclassNameに、複数のクラスを簡単に指定する（Classnames利用）](https://www.yoheim.net/blog.php?q=20180701)
- あとはこの`highlight-color`を条件分岐で表示させられたら良さそう

とはいえ、どこからこの情報を持って来ればいい？どういう条件で表示する？ってとろこで少し悩みました

- データはprops経由で親から子へ渡る
    - チュートリアルを見返すと

> Board の renderSquare メソッド内で、props として value という名前の値を Square に渡すようにコードを変更します：

- もう一回コードをよく見てみると
    - `Square`で表示しているのは`props.value`
    - こいつはどこの値かというと`Board`内の <Square ... />内の`value={ this.props.squares[i] }`

```jsx

class Board extends React.Component {
  renderSquare(i) {
    return <Square
              value={ this.props.squares[i] }
              onClick={ () => this.props.onClick(i) }
              key={i}
            />;
  }
 ...
```
- じゃあこの`this.props.squares[i]`はどこから来てるんだ
- `Game`内の <Board ... />内の`squares={current.squares}`っぽい

```jsx

return (
  <div className="game">
    <div className="game-board">
      <Board
        squares={current.squares}
        onClick={(i) => this.handleClick(i)}
  />
...
```

コードを読み返してわかったこと：

- `Game` -> `Board` -> `Square`と`props`が渡ってるので
- `Game`内の`<Board ... />` -> `Board`内の`renderSquare(i)（<Square ... />）` -> `Square`の`<button>`と値を渡してやれば良さそう

というわけで

- 「もし、勝ったラインの配列に`Square`の`i`があったら、色をつける」みたいな条件にするとしたら
    1. `Game` -> `Board` へは`勝ったラインの配列`を
    2. `Board`内の`renderSquare(i)`で渡ってきた配列に`i`が含まれているか（`true`/`false`）を
    3. `Square`で渡って来た値が`true`だったら色をつける、とする

という方針で進めることにしました

#### 1. `Game` -> `Board` へ`勝ったラインの配列`を渡す

- 勝ち判定を`if (winnerInfo) { 〜`の部分でしているので、勝った場合`winnerInfo.line`を入れる部分を作ってやる

```jsx

let status;
let winLine = []; // 追加
if (winnerInfo) {
  status = `Winner: ${winnerInfo.player}`; 
  winLine = winnerInfo.line; // 勝ち判定が出た場合ここに勝ちにつながったlineを入れる 
} else {
  status = `Next player: ${this.state.xIsNext ? 'X' : 'O'}`;
} 
```
```jsx

return (
  <div className="game">
    <div className="game-board">
      <Board
        squares={current.squares}
        onClick={(i) => this.handleClick(i)}
        winLine={winLine} // ここでpropsとしてBoardにwinLineを渡す
  />
```

#### 2. `Board`内の`renderSquare(i)`で渡ってきた配列に`i`が含まれているか（`true`/`false`）を渡す
          
- 配列の中に含まれているかどうか、を調べるのは下記を参考に
    - [Array.prototype.includes() - JavaScript | MDN - Mozilla](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/includes)

```jsx

renderSquare(i) {
  return <Square
            value={ this.props.squares[i] }
            onClick={ () => this.props.onClick(i) }
            isHighlight={ this.props.winLine.includes(i) } // 渡って来た配列にiが含まれているかどうか
            key={i}
          />;
}
```

#### 3. `Square`で渡って来た値が`true`だったら色をつける

```jsx

<button
  className={
    props.isHighlight ? `square highlight-color` : `square` // trueなら highlight-color を反映する
    }
  onClick={props.onClick}
>
```

なんとかできました！！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/bc5a8bbc-cb18-c011-e1c6-97c0829af4df.png)

## 6.どちらも勝利しなかった場合、結果が引き分けになったというメッセージを表示する。

これもまずさらっと実装方針を考えてみます

- 勝ち負け判定に`Draw`を追加して`status`の部分で条件分岐を追加して表示させてあげる
- 勝ち負け判定は`calculateWinner(squares)`でやっていて`squaers`には盤面の情報が入っている
- `squaers`の`null`がなくなった時（盤面が全て埋まった時）が`Draw`ということとする

```jsx

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 { player: squares[a], line: [a, b, c] };
    }
  }
  // ここでDraw判定
  if (!squares.includes(null)) {
    return 'draw';
  }
  return null;
}
```

これで

- どちらかが勝った時
    - `{ player: 'X' or 'O', line: [a, b, c] }`
- 引き分けの時
    - `'draw'`
- 決着が付いていない時
    - `null`

が返ってくるようになりました
条件分岐の順番を少し並び替えて下記のようにしたら…

```jsx

let status;
let winLine = [];
if (winnerInfo === null) {
  status = `Next player: ${this.state.xIsNext ? 'X' : 'O'}`;
} else if (winnerInfo === 'draw') {
  status = `Draw`; 
} else {
  status = `Winner: ${winnerInfo.player}`; 
  winLine = winnerInfo.line;
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242529/31f149dc-7fc1-e580-ea33-963c4f0dcd9d.png)

引き分け時に`Draw`と表示できました！！
これで全部の追加課題が終了
お疲れさまでした=^o^=

# おわりにちょっとした感想など

- もっとリファクタ＆改造できるところはあると思うが、とりあえずちゃんと表示されて動くものを作れるのが目標だったので、達成できてよかった
- class間の（props）値の受け渡しの流れがまだふんわりとしかつかめていないのでので復習したい
- でも動くもの作れたのはすごく楽しい
- 情報を管理できるstateすごい…Reactだけで簡単なゲームが作れちゃうんだなぁ
- 基本的なJS（ES6）がわかればできるのでReactチュートリアルすごい

まだまだ基礎が学べただけだと思うので、これからも勉強を続けて実務でコード書けるように頑張ります！
（そしていつかこのコードを改造＆リファクタしたい）