Reactで神経衰弱アプリ作ってみた（リファクタリング編）

## 企画概要
今アツいReactを使って、みんなでアプリを作ってみよう！という思いから始まった神経衰弱アプリ作成企画…

全6回でお送りしてきましたが、今回が最終回！！！

**リファクタリング**編です

関連記事は以下です
興味のある方は是非シリーズで読んでみてください

* [決起編](https://qiita.com/ntc-suzuki/items/713ee53825e833ab2a19)
* [フロント環境構築編](https://qiita.com/ntc-arai/items/06344ef8522205c9936d)
* [フロントエンド編](https://qiita.com/ntc-okada/items/4db14c21a06053ca2a90)
* [バックエンド編](https://qiita.com/ntc-suzuki/items/34eb7ff436326b2c88b7)
* [コンポーネント作成編](https://qiita.com/ntc-arai/items/d2a1bb152b5a0859ac9e)
* リファクタリング編 ★本記事

 
# リファクタリング
ついに神経衰弱ができるようになりました！　
Reactの経験が浅いチームでしたが、ここまでこれました！感動です
しかし、あんまりゲームっぽくはないような？？？
さらに内部のコードも読みにくい…
ということで、ここからはブラッシュアップの時間です

## コードが長い

例えば…
```top.tsx
 import {
  Button,
  Typography,
  Stack,
} from "@mui/material";
import { Link } from "react-router-dom";

const Top = () => {
  return (
    <div>
      <div
        style={{
          display: "flex",
          justifyContent: "center",
          marginTop: "200px",
        }}
      >
        <Typography sx={{ fontSize: "4rem", color: "#65BBE9" }}>M</Typography>
        <Typography sx={{ fontSize: "4rem" }}>emory</Typography>
        <Typography sx={{ fontSize: "4rem", color: "#65BBE9" }}>T</Typography>
        <Typography sx={{ fontSize: "4rem" }}>racer</Typography>
      </div>
      <div
        style={{
          display: "flex",
          justifyContent: "center",
        }}
      >
        <Typography sx={{ fontSize: "2rem", fontWeight: "bold" }}>
          神経衰弱
        </Typography>
      </div>
      <div
        style={{
          display: "flex",
          justifyContent: "center",
          marginTop: "150px",
        }}
      >
        <Stack>
          <Link to="/game">
            <Button
              variant="outlined"
              sx={{ width: "200px", fontSize: "19px" }}
            >
              Game Start
            </Button>
          </Link>
        </Stack>
      </div>
    </div>
  );
};
```
**うん　長い**

この規模の実装なら「めちゃくちゃ読みにくい！」とはなりませんがもっと大きくなると確実に目に優しくなくなるのは目に見えてます
せっかくReactを使っているということもあるので全て[コンポーネント化](https://mui.com/system/styled/)していきましょう


```components/element.tsx
import styled from "@emotion/styled";
import {
    Button,
    Typography,
  } from "@mui/material";
 
export const TopStyle = styled("div")({
    display: "flex",
    justifyContent: "center",
    marginTop: "200px",
})

export const SubStyle = styled("div")({
    display: "flex",
    justifyContent: "center",
})

export const ChoiceStyle = styled("div")({
    display: "flex",
    justifyContent: "center",
    marginTop: "150px",
})

export const SelectButton = styled(Button)({
    variant: "outlined",
    width: "200px", 
    fontSize: "19px" ,
})

export const LightBlueText = styled(Typography)({
    fontSize: "4rem",
    color: "#65BBE9" 
})

export const BlackText = styled(Typography)({
    fontSize: "4rem"
})

export const SubTitleText = styled(Typography)({
    fontSize: "2rem", 
    fontWeight: "bold"
})
```
今回はtop.tsxの文字数をいかに減らすか焦点を置いているのでpropsは使用していません

このようにcss部分のみ別ファイルに切り出して置くと…
```top.tsx
import {
  Stack,
} from "@mui/material";
import { Link } from "react-router-dom";
import { TopStyle, SubStyle, ChoiceStyle, SelectButton, LightBlueText, BlackText, SubTitleText } from "../components/element";

const Top = () => {
  return (
    <>
      <TopStyle>
        <LightBlueText>M</LightBlueText>
        <BlackText>emory</BlackText>
        <LightBlueText>T</LightBlueText>
        <BlackText>racer</BlackText>
      </TopStyle>
      <SubStyle>
        <SubTitleText>
          神経衰弱
        </SubTitleText>
      </SubStyle>
      <ChoiceStyle>
        <Stack>
          <Link to="/game">
            <SelectButton variant="outlined">
              Game Start 
            </SelectButton>
          </Link>
        </Stack>
      </ChoiceStyle>
    </>
  );
};
```
とてもすっきりしました

**コンポーネント名の先頭は大文字じゃないとReactは認識しない**ところが注意点です
文字の装飾をまとめて変えたいときも元のコンポーネントを変えるだけなので楽ですね


## プレイヤーを焦らせたい
残り時間が少なくなると表示の色が変わると焦りも出やすくなりタイムアタックゲームぽくなるのではないでしょうか？SASUKEのあれっぽいやつです

音こそ出ませんがせっかくなのでプレイヤーを焦らせていきましょう

```React
  useEffect(() => { 
  const area=document.getElementById("time");
  if(area !== null)
   // countTime = 制限時間
   if(countTime <= 10){
    area.style.color="red";
  }else{
    area.style.color="black"
  }
})

 return(
   ゲーム残り時間:
  <span id = "time" >{countTime % 90}秒</span>
  )
```
getElementByIdで"time"というidが付与されているhtml要素を取得しています
SASUKEリスペクトなので制限時間が10秒切ったときに<font color="Red">赤く</font>なるよう実装しています

## いろんな難易度で遊びたい
12組24枚を60秒以内に揃えきってクリアモーダルを拝むのはちょっと厳しい…(少なくとも私は)
もうちょっと簡単なモードも欲しい！ということで作りました
```easyGame.tsx
//　(変更部分のみ抜粋)

// カードの枚数
const NUMBEROFCARDS = 16;

// カード生成関数
const generateCards = (): CardData[] => {
  const values = ["A", "B", "C", "D", "E", "F", "G", "H"];
  const cards = values.flatMap((value) => [
    { id: Math.random(), value },
    { id: Math.random(), value },
  ]);
  return shuffle(cards)
};

 // カードを並べるためのレイアウトを設定
  const rows = [];
  for (let i = 0; i < 4; i++) {
    rows.push(
      <div key={i} style={{ display: "flex", justifyContent: "center" }}>
        {cards.slice(i * 4, (i + 1) * 4).map((card) => (
          <Card
            key={card.id}
            id={card.id}
            value={card.value}
            // 以下評価結果がtrueだと裏になる。
            isFlipped={
              flippedCards.includes(card.id) || matchedCards.includes(card.id)
            }
            onClick={handleCardClick}
          />
        ))}
      </div>
    );
  }

```
作るといってもカードの総枚数を8種16枚に変えて、カードの枚数に合わせてレイアウトを4×4に変更しただけで他のロジックは使いまわしです


一応、神経衰弱神のために13種52枚のトランプを踏襲したモードも作っておきますか…
```hardGame.tsx
//　(変更部分のみ抜粋)

// カードの枚数
const NUMBEROFCARDS = 52;

// カード生成関数
const generateCards = (): CardData[] => {
  const values = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"];
  const cards = values.flatMap((value) => [
    { id: Math.random(), value },
    { id: Math.random(), value },
    { id: Math.random(), value },
    { id: Math.random(), value },
  ]);
  return shuffle(cards)
};

 // カードを並べるためのレイアウトを設定
  const rows = [];
  for (let i = 0; i < 4; i++) {
    rows.push(
      <div key={i} style={{ display: "flex", justifyContent: "center" }}>
        {cards.slice(i * 13, (i + 1) * 13).map((card) => (
          <Card
            key={card.id}
            id={card.id}
            value={card.value}
            isFlipped={
              flippedCards.includes(card.id) || matchedCards.includes(card.id)
            }
            onClick={handleCardClick}
          />
        ))}
      </div>
    );
  }
  ......
    ゲーム残り時間:
  <span id = "time" >{countTime % 90}秒</span>
```
先ほどと同じように基本的なロジックは使いまわしです
カードの枚数に加えて制限時間の表示部分とレイアウトを変更しています


```
{ id: Math.random(), value }
```
を4つに増やすことで1つの値に対してカードを4枚ずつ("A"が4枚、"2"が4枚、"3"が4枚......)生成しています
それに合わせてレイアウトを4×13に設定しています


```
    ゲーム残り時間:
  <span id = "time" >{countTime % 90}秒</span>
```
カードの総枚数が大幅に増えているので制限時間を90秒に伸ばしています

この難易度ではたして誰がクリアできるんでしょうか!?!?!?

## 難易度選択したい

現在は12種24枚のモード(以下ノーマルモード)しか遊べないので、今回の8種16枚のモード(以下イージーモード)と13種52枚のモード(以下ハードモード)も遊べるように難易度選択画面を実装します。
(難易度という概念が生まれたのでLink toの向き先を/gameから/normalGameにこっそり変えています)

```selectDifficultyOfGame.tsx
import { Link } from "react-router-dom";
import {
    Stack,
  } from "@mui/material";
  import { TopStyle, SubStyle, ChoiceStyle, SelectButton, LightBlueText, BlackText, SubTitleText } from "../components/element";

const SelectDifficultyOfGame = () => {
    return(
    <>
        <TopStyle>
            <LightBlueText>S</LightBlueText>
            <BlackText>elect</BlackText>
            <LightBlueText>D</LightBlueText>
            <BlackText>ifficulty</BlackText>
        </TopStyle>
        <SubStyle>
            <SubTitleText>難易度選択</SubTitleText>
        </SubStyle>
        <ChoiceStyle>
            <Stack>
                <Link to="/easyGame">
                    <SelectButton
                    variant="outlined">
                    Easy
                    </SelectButton>
                </Link>
            </Stack>
            <Stack>
                <Link to="/normalGame">
                    <SelectButton
                    variant="outlined">
                    Normal
                    </SelectButton>
                </Link>
            </Stack>
            <Stack>
                <Link to="/hardGame">
                    <SelectButton
                    variant="outlined">
                    Hard
                    </SelectButton>
                </Link>
            </Stack>
        </ChoiceStyle>
    </>
    )

}
```
なんと先ほどコンポーネント化したおかげでここでもcssを使いまわせています
コンポーネント化っていいですね、時短にもなりますし便利です

### 完成画面
完成した難易度選択画面がこちら↓↓↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3883523/fb6feadc-66ec-27a0-a3d9-41de7cbec598.png)

イージーモードゲーム画面↓↓↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3883523/071deea4-aa3c-47a0-d9d1-b85ebf1da54d.png)
ハードモードゲーム画面↓↓↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3883523/c4ca9ccf-d44f-c9a5-c93e-4b0a81070c94.png)

ハードモードの無理ゲー感が…笑

難易度選択までできるとちゃんとゲームっぽくなりましたね！！！！！