Next.jsで保守性の高いAlertBoxのコンポーネントを作成する

## 今回作成するもの
![スクリーンショット 2021-07-21 1.15.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/825833/3ee1d48a-cf05-dcd5-eff8-9674dd18eaa5.png)

このようなよくあるAlertBoxのcomponentを作成していきます。
最近はcssフレームワークで簡単に部品を提供してくれていますが、現場によっては自分で作成していく部分も多々あります！
目標としてはただ作るだけではなく、なるべく綺麗に保守性の高いコードを意識していきます。

## 前提知識

- React, Next.jsの基礎
- Componentの理解
- TypeScriptの基礎

## 対象

- React初学者
- バックエンドをメインにやっているがフロントエンドも触りたい


## 早速やってみよう！

今回はNext.js, TypeScript, Sassを使用しcssはcss moduleベースで書いていきます。

ルートディレクトリにcomponentディレクトリを作成し、AlertBoxディレクトリを作成します。
AlertBoxディレクトリの中に
- AlertBox.tsx
- AlertBox.module.scss
を作成しましょう。
ちなみにSassは

```
npm install --save sass
```
でインストールしておきましょう。

ではさくっと完成系のコードを載せてから説明していきます。

```components/AlertBox/AlertBox.tsx
import React, {memo, useState, VFC} from "react";
import styled from "./AlertBox.module.scss";


type AlertProps = {
  title: string;
  description: string;
  alertType: "ERROR" | "INFO" | "WARNING" | "SUCCESS"
}

export const AlertBox: VFC<AlertProps> = memo((props) => {
  const { title, description, alertType } = props;

  const [closeAlertBox, setCloseAlertBox] = useState<boolean>(true);

  const alertTypeModifierCSS_Class = (): string | null => {
    switch (alertType) {
      case "ERROR": return styled.alertBox__Error;
      case "INFO": return styled.alertBox__Info;
      case "WARNING": return styled.alertBox__Warning;
      case "SUCCESS": return styled.alertBox__Success;
      default: return null;
    }
  }

  const onclickCloseAlertBox = (): void => {
    setCloseAlertBox(!closeAlertBox)
  }

  return (
    <>
      {closeAlertBox &&
        <div className={`${alertTypeModifierCSS_Class()} ${styled.alertBox}`}>
          <div className={styled.alertBody}>
            <div className={styled.alertTitle}>{ title }</div>
            <div className={styled.alertDescription}>{ description }</div>
          </div>
          <div className={styled.closeButton}></div>
          <span
            className={styled.closeButton}
            onClick={onclickCloseAlertBox}
          >{ '×' }</span>
        </div>
      }
    </>
  );
});

```

まずは上からReactをインポートし、memo, useState, VFCをインポートし(後で説明します)、styleをインポートしておきましょう。
これでスタイルが読み込めます。
styledは任意の名前なのでなんでもおっけいです！
自分はstyledにしています。
classesとかにしているものよくみますね。


### propsの型定義

```
type AlertProps = {
  title: string;
  description: string;
  alertType: "ERROR" | "INFO" | "WARNING" | "SUCCESS"
}
```
ここでまずpropsの型を宣言しています。
AlertBoxのタイトルと説明、そして今回は4つのタイプを作成していくので

- error(エラー)
- info(お知らせ)
- warning(警告)
- success(成功)

基本的にこの4パターンで構成されることが多いと思うのでこちらで行っていきます！

```
export const AlertBox: VFC<AlertProps> = memo((props) => {}
```
こちらはAlertBoxをFunctional Componentで定義しています。
Functional Componentの型定義はVFCで行います。
これは 一番上のReactからimponetしていますね。
FCはFunctional Componentの略で、VFCはFCの強化版程度に思っていてください。

```
VFC<AlertProps>
```
これで先ほど定義した型をpropsに設定しています。

### memo

Reactで結構やりがちなのが無駄なサイレンダリングが起きてしまうことです。
サイレンダリングが起きれば起きるほどページの速度は遅くなります。

サイレンダリングが起きる条件は大きく3つあります。

- stateが更新された時
- propsの値が更新された時
- Componentがサイレンダリングされた際のComponent配下の子要素

問題はこちらです。
”Componentがサイレンダリングされた際のComponent配下の子要素”
つまりAlertBoxが使われているComponentがサイレンダリングされるとAlertBoxもサイレンダリングされてしまうということです、、、
これは避けたいですね。

memoで囲んであげるとこれを解決することができます！

```
const { title, description, alertType } = props;
```
こちらでpropsをまとめています。
本来は{ props.title }などと書かなければですが、こちらで{ title }だけで済むようになります。

### state 部分

```
const [closeAlertBox, setCloseAlertBox] = useState<boolean>(true);
```
こちらでstateを管理しています。
今回はAlertBoxを削除する×ボタンを配置しているので、真偽知を設定しています.
useState<boolean>で型を設定していますが、こちらは型推論もあるので書かなくても問題はないです。

ただ自分の経験上型推論に頼ると、型定義しなければいけないところで怠っているケースを多々みてきたので、基本的に全て型定義するようにしています。

このあたりは現場ではチームのルールに従って書くのが良いと思います！！

先にreturn配下のJSX部分をみていきます！

```component/AlertBox/AlertBox.tsx
  return (
    <>
      {closeAlertBox &&
        <div className={`${alertTypeModifierCSS_Class()} ${styled.alertBox}`}>
          <div className={styled.alertBody}>
            <div className={styled.alertTitle}>{ title }</div>
            <div className={styled.alertDescription}>{ description }</div>
          </div>
          <div className={styled.closeButton}></div>
          <span
            className={styled.closeButton}
            onClick={onclickCloseAlertBox}
          >{ '×' }</span>
        </div>
      }
    </>
  );
```

```
{closeAlertBox &&　 左辺がtrueならば右を返す}
```
という意味です。

```
const [closeAlertBox, setCloseAlertBox] = useState<boolean>(true);
```

closeAlertBoxはstateで初期値をtrueに設定してあるので、初めは表示されますね！

```
<span
  className={styled.closeButton}
  onClick={onclickCloseAlertBox}
>{ '×' }</span>
```
この×ボタンのところでクリックイベントを使ってtrue, falseを変えています！

```
const onclickCloseAlertBox = (): void => {
  setCloseAlertBox(!closeAlertBox)
}
```
クリックされるとイベントが発火しsetCloseAlertBoxの処理が行われます！
こちらでfalseになるのでAlertBoxの表示が消えます！！

### switch文で4種類に分ける
実務に入る前はswitch分ってどんな時に使うんだろうって思っていたんですが、かなり使えます。

今回は4種類に定義していきたいのでswitch分でクラスを指定し、styleを変えていきます！

```ts
  const alertTypeModifierCSS_Class = (): string | null => {
    switch (alertType) {
      case "ERROR": return styled.alertBox__Error;
      case "INFO": return styled.alertBox__Info;
      case "WARNING": return styled.alertBox__Warning;
      case "SUCCESS": return styled.alertBox__Success;
      default: return null;
    }
  }
```
何も選択されなかった場合を想定してdefaultをnullで返しています。
なので型も

```
string |(または) null
```
ですね。

ちなみにcss moduleで二つのクラスを定義する場合はこう書きます。

```tsx
<div className={`${alertTypeModifierCSS_Class()} ${styled.alertBox}`}>
```
alertTypeModifierCSS_Class()を指定することでpropsで渡ってくる値によってstyleを変更します。

## スタイリング

```AlertBox.module.scss
// - Type別のスタイル ====================== //

.alertBox__Error {
  background: #FFE1E0;
  color: #D81E00;

}

.alertBox__Info {
  background: #E0F7FF;
  color: #075C99;
}
.alertBox__Warning {
  background: #FFF0D6;
  color: #F5A623;
}
.alertBox__Success {
  background: #E5FEE0;
  color: #339F69;
}

// - 共通スタイル ====================== //

.alertBox {
  display: flex;

  width: 610px;
  height: 97px;

  .alertBody {
    display: flex;
    flex-direction: column;
    justify-content: center;

    padding-left: 60px;
  }

  .alertTitle {
    font-size: 18px;
    font-weight: bold;
  }

  .alertDescription {
    padding-top: 14px;

    font-size: 14px;
  }

  .closeButton {

    margin-top: 10px;
    margin-right: 10px;
    margin-left: auto;

    cursor: pointer;
  }

}
```
こんな感じです！


先ほどswitch文で定義したクラスをこちらでそれぞれ振り当て、背景色、文字色を変えています！

```
// - Type別のスタイル ====================== //

.alertBox__Error {
  background: #FFE1E0;
  color: #D81E00;

}

.alertBox__Info {
  background: #E0F7FF;
  color: #075C99;
}
.alertBox__Warning {
  background: #FFF0D6;
  color: #F5A623;
}
.alertBox__Success {
  background: #E5FEE0;
  color: #339F69;
}
```

ではこちらで完成したので、みていきましょう！！

```pages/index.tsx
import {AlertBox} from "../components/orfanism/alertBox/AlertBox";

export default function Home() {
  return (
    <div>
      <AlertBox
        title={"エラー重要なメッセージ"}
        description={"エラーについての文章が入ります。エラーについての文章が入ります。"}
        alertType={"ERROR"}
      />
      <AlertBox
        title={"お知らせのメッセージ"}
        description={"お知らせについての文章が入ります。お知らせについての文章が入ります。"}
        alertType={"INFO"}
      />
      <AlertBox
        title={"警告のメッセージ"}
        description={"警告についての文章が入ります。警告についての文章が入ります。"}
        alertType={"WARNING"}
      />
      <AlertBox
        title={"成功のメッセージ"}
        description={"成功についての文章が入ります。成功についての文章が入ります。"}
        alertType={"SUCCESS"}
      />
    </div>
  );
};

```

![スクリーンショット 2021-07-21 1.15.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/825833/3ee1d48a-cf05-dcd5-eff8-9674dd18eaa5.png)

あとは自分でスタイルの変更などアレンジしてみましょう！
お疲れさまでした！！



#　完成系のコード

ごちゃごちゃしないように最後に完成系のコードを載せておきます。

```components/AlertBox/AlertBox.tsx
import React, {memo, useState, VFC} from "react";
import styled from "./AlertBox.module.scss";


type AlertProps = {
  title: string;
  description: string;
  alertType: "ERROR" | "INFO" | "WARNING" | "SUCCESS"
}

export const AlertBox: VFC<AlertProps> = memo((props) => {
  const { title, description, alertType } = props;

  const [closeAlertBox, setCloseAlertBox] = useState<boolean>(true);

  const alertTypeModifierCSS_Class = (): string | null => {
    switch (alertType) {
      case "ERROR": return styled.alertBox__Error;
      case "INFO": return styled.alertBox__Info;
      case "WARNING": return styled.alertBox__Warning;
      case "SUCCESS": return styled.alertBox__Success;
      default: return null;
    }
  }

  const onclickCloseAlertBox = (): void => {
    setCloseAlertBox(!closeAlertBox)
  }

  return (
    <>
      {closeAlertBox &&
        <div className={`${alertTypeModifierCSS_Class()} ${styled.alertBox}`}>
          <div className={styled.alertBody}>
            <div className={styled.alertTitle}>{ title }</div>
            <div className={styled.alertDescription}>{ description }</div>
          </div>
          <div className={styled.closeButton}></div>
          <span
            className={styled.closeButton}
            onClick={onclickCloseAlertBox}
          >{ '×' }</span>
        </div>
      }
    </>
  );
});
```

```AlertBox.module.scss
// - Type別のスタイル ====================== //

.alertBox__Error {
  background: #FFE1E0;
  color: #D81E00;

}

.alertBox__Info {
  background: #E0F7FF;
  color: #075C99;
}
.alertBox__Warning {
  background: #FFF0D6;
  color: #F5A623;
}
.alertBox__Success {
  background: #E5FEE0;
  color: #339F69;
}

// - 共通スタイル ====================== //

.alertBox {
  display: flex;

  width: 610px;
  height: 97px;

  .alertBody {
    display: flex;
    flex-direction: column;
    justify-content: center;

    padding-left: 60px;
  }

  .alertTitle {
    font-size: 18px;
    font-weight: bold;
  }

  .alertDescription {
    padding-top: 14px;

    font-size: 14px;
  }

  .closeButton {

    margin-top: 10px;
    margin-right: 10px;
    margin-left: auto;

    cursor: pointer;
  }

}

```