React tutorial(リアクト チュートリアル)やってみた！！追加要素編

# 初めに
インターン先の課題で***React tutorial***をやることになったのでその過程で学んだことを書いていきます。
普通にやるだけではつまらないのでいくつか追加要素を加えてみました。
私が作成したアプリはこちら => https://xogame-mobile.herokuapp.com/
＊モバイル対応実装しました＊

### サンプル画像
![デモ画面](https://qiita-image-store.s3.amazonaws.com/0/178746/b584baba-a19f-3773-0be6-99fc128783f7.png)


この記事は公式版のReact tutorialが完成した前提で話を進めます。
完成していない方は[公式サイト(英語)](https://reactjs.org/tutorial/tutorial.html)もしくは[解説サイト(日本語版)](https://mae.chab.in/archives/2943)を元に頑張ってください。

今回僕が追加した要素は
 - カーソルが乗ったブロックの色を変える
 - マス目を3x3から5x5に変える(同時に勝利条件を「5個連続する」に変える)
 - 勝敗が決した時にラインに色をつける
 - プレイヤーに対応して色を変える
の4つです。

以下、順に解説します。
- - - -

## 1, カーソルが乗ったブロックの色を変える

これは4つの中で最も簡単です。
index.cssファイルの中に、どこでもいいので
`.square:hover {background-color: #C0C0C0;}`
を追加してみてください。
(今回は灰色にしたかったので#C0C0C0と書きましたが、各自の好みで変更してください => [cssの色一覧表](http://www.netyasun.com/home/color.html))
今回は`:hover`というcssセレクタを使用しました。
これはカーソルが乗った時にcssを当ててくれるものです。

これによってカーソルが乗った部分が灰色になりました。

## 2, マス目を3x3から5x5に変える(同時に勝利条件を適宜変更)

これも4つの中で簡単な部類です。
このtutorialではBoardクラス内でマス目を定義しているので、そこに変更を加えます。

index.jsファイルのBoardクラスに移動してください。
render()以下で*board-row*という名前のクラスでブロック9つが定義されています。初期では
`{this.renderSquare(0)}`から`{this.renderSquare(8)}`までありますが、これを`{this.renderSquare(0)}`から`{this.renderSquare(24)}`まで定義するようにします。
(初期では3つずつdivで括られていたのを5つずつにすることを忘れずに)

こうすることでcssも上手く当たり、マス目が5x5に変わります。

マス目が5x5になったので勝利条件も変えていきます。
BoardクラスからGameクラスへ移動してください。
最初のconstructor()の下で
`squares: Array(9).fill(null)`としているところを`squares: Array(25).fill(null)`とします。

これにより、5x5マス分の変化(空欄or"X"or"O")が記録されるようになります。

そして、最後に**function calculateWinner(squares)**の中身に手を加えます。
	まず、最初の`const lines=[[...],[...],...];`の部分を

```
const lines = [
    [0, 1, 2, 3, 4], [5, 6, 7, 8, 9],[10, 11, 12, 13, 14],[15, 16, 17, 18, 19],
    [20, 21, 22, 23, 24],[0, 5, 10, 15, 20],[1, 6, 11, 16, 21],[2, 7, 12, 17, 22],
    [3, 8, 13, 18, 23],[4, 9, 14, 19, 24],[0, 6, 12, 18, 24],[4, 8, 12, 16, 20]
];
```

に書き変えます。ここでは勝利する際の並び方の候補を定義しています。
ちゃんと12種類もれなく書いてあげることに注意してください。
その下のfor文も

```
for (let i = 0; i < lines.length; i++) {const [a, b, c, d, e] = lines[i];

if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c] && squares[b] === squares[d] && squares[c] === squares[e]) {
 return { winner: squares[a]};}}return null;
```

と書き変えてください。
ここではクリックされているブロックの位置とconst linesで定義した12種類を比較して、どちらかのプレイヤーが勝利した時点で勝者名を宣言するようにしています。

これで3x3マスで動いていたアプリが5x5マスでも動くようになりました。

## 3, 勝敗が決した時にラインに色をつける

分かりにくいと思うので↓こういうことです↓
![スクリーンショット 2017-11-05 19.23.40.png](https://qiita-image-store.s3.amazonaws.com/0/178746/b3cd5748-447f-39c8-070d-1da610bc4968.png)
黄色で表示することでどのラインが揃ったか分かりやすくなっています。
これを実装するのは少々面倒です。
まずは、この前のセクションで変更したばかりの*function calculateWinner(squares)*の中身に再び手を加えます。
	for文を

```
for (let i = 0; i < lines.length; i++) {
    const [a, b, c, d, e] = lines[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c] && squares[b] === squares[d] && squares[c] === squares[e]) {
      return { winner: squares[a], line: [a ,b, c, d, e] };
    }
  }
```

に書き変えてください。

次に、Gameクラスのrender()以下にこの3行を追加してください。
`const winnerData = calculateWinner(current.squares);`
`const winner = winnerData ? winnerData.winner : null;`
`const winnerLine = winnerData ? winnerData.line : [];`

さらに、returnの中のsquaresやonClickと同じところに
`winnerLine={winnerLine}`
を書き加えてください。

さらにBoardクラスのreturnの中身のvalueやonClickと同じところに
`highlight={this.props.winnerLine.includes(i)}`
を追加してください。

最後にSquareファンクションの中身のif文の前半を以下のようにします。

```
if (props.highlight) {
    return (
      <button className="square" onClick={() => props.onClick()} style={{color: "yellow"}}>
        {props.value}
      </button>
    );
```

ここまでの操作をまとめて説明します。
calculatewinnerファンクションからwinnerについての情報がreturnされると、winnerDataにXもしくはOという情報が入り、「真」になることで勝者宣言する部分のwinnerがnullからwinnerData.winnerに変わり、winnerLineでcalculatewinnerファンクションで返すようにしたline:[a, b, c, d, e]をwinnerlineで呼べるようになります。
そして、このwinnerlineは`<Board/>`の中に書くことでBoardクラスと結ばれます。
Boardクラス内でwinnerlineを使ってhighlightを定義して、それを使ってstyleで黄色を当てています。

これで勝敗が決した時にラインに色が付くようになりました。

## 4, プレイヤーに対応して色を変える

僕が一番苦戦したのがこれです。

初めに、Gameクラス内のhandle clickコンポーネントの中の
`squares[i] = this.state.xIsNext ?  "X" : "O";`　を
`squares[i] = this.state.xIsNext ?  <div style={{color:"red"}}>X</div> : <div style={{color:"blue"}}>O;`
に変更してみました。
すると、見事に「X」は赤色に「O」は青色に変化してくれたのですが、肝心の勝敗を決める機能が上手くいかなくなってしまいました。

次に、他に`<div></div>`で括れるものがないか探し始めましたが、結果は芳しくありませんでした。

最後に試したのが冒頭Squareファンクション内部のif文に変更を加えることです。
具体的にどうやったかは以下のコードを参考にしてください。

```
function Square(props) {
  if (props.highlight) {
    return (
      <button className="square" onClick={() => props.onClick()} style={{color: "yellow"}}>
        {props.value}
      </button>
    );
  } else{
	  if(props.value === 'X') {
      return (
        <button className="square" onClick={() => props.onClick()} style={{color: "red"}}>
          {props.value}
        </button>);}
	  else {
		return(
			<button className="square" onClick={() => props.onClick()} style={{color: "blue"}}>
				{props.value}
			</button>
		);
	  }
	}
}
```

これによって、`props.value`に渡されるものが
	“X” ならば赤色
	“O” ならば青色	
とすることができました。	
（ ”X” , ”O” 以外の時はどちらかが勝利した時なので前のセクションで黄色くしています）

## おわりに

React tutorial に追加要素を加えてみて、既に完成されているtutorial本体を順を追ってやっていくよりも様々な問題にぶつかり、その分だけ成長できたのではないかと思います。

## 参考にしたもの

[React tutorial 本編](https://reactjs.org/tutorial/tutorial.html)
[CSSセレクタ一覧](http://www.htmq.com/csskihon/005.shtml)