型でButtonのスタイルを切り替える方法を変更してみた。


## はじめに

親要素から Button コンポーネントに variant を渡して Button のスタイルを変更させていたものを下のように変更した。

- **変更前:MUI の Button がデフォで持っていた variant によって Button のスタイルを変更させる方法**
- **変更後:新たに Button に ui という型を持たせて Button のスタイルを変更させる方法**
  ![ButtonComponent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3337943/3ab5b24d-be10-c143-3d37-616a5219a5d7.png)

さっそくコードと画像を用いて説明する。

## 前提知識

変更点を説明する前の前提知識として、MUI の Button は`variant`という`props`を受け取れる。variant で受け取った値によって下記のように変更できる。
![standard MUI Button.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3337943/78dc0c42-dffb-b8bb-f120-442af3ca1050.png)

```typescript
<Button variant="text">Text</Button>
<Button variant="contained">Contained</Button>
<Button variant="outlined">Outlined</Button>
```

## 変更前

変更前は、前提知識で紹介した MUI の Button がデフォルトで持っている`variant`の値によってスタイルを以下のようにラップしていた。

```typescript:hogeButton.tsx
export default function HoggButton() {
  interface HoggType {
    variant: string;
  }

  const HoggButton = styled(MuiButton)<HoggType>`
    width: 200px;
    padding: 10px;
    ${(props) => {
      if (props.variant === "outlined") {
        return css`
          background-color: #000;
          border: 5px solid green;
          &:hover {
            background-color: #fff;
            border: 5px solid red;
          }
        `;
      } else if (props.variant === "text") {
        return css`
          color: red;
          background-color: #fff;
          border: 5px solid yellow;
          &:hover {
            background-color: #fff;
            border: 5px solid blue;
          }
        `;
      }
    }}
  `;

  return (
    <div
      style={{ border: "5px solid red", padding: "5px", marginBottom: "5px" }}
    >
      <p style={{ textAlign: "center" }}>variant</p>
      <div
        style={{
          width: "100vw",
          display: "flex",
          justifyContent: "center",
          alignItems: "center",
          gap: "20px",
        }}
      >
        <HoggButton variant={"contained"}>variant={"contained"}</HoggButton>
        <HoggButton variant={"outlined"}>variant={"outlined"}</HoggButton>
        <HoggButton variant={"text"}>variant={"text"}</HoggButton>
      </div>
    </div>
  );
}
```

**ビジュアルで見ると下のようなイメージ**
![variant.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3337943/05d0b6b9-40ea-1052-4029-86c6e3a4cd08.gif)

## 変更後

元々 MUI で持っていた`variant`でのスタイル変更を辞め、新たに`ui`という型を作った。
値は、`primary`、`secondary`という`string`を受け取れるように宣言した。

```typescript:fooButton.tsx
  export default function FooButton() {
  interface FooType {
    ui: "primary" | "secondary";
  }

  const FooButton = styled(MuiButton)<FooType>`
    width: 200px;
    padding: 10px;

    ${(props) => {
      if (props.ui === "primary") {
        return css`
          background-color: #fff;
          border: 5px solid blue;
          border-radius: 20px;
          &:hover {
            color: #909;
            border: 5px solid #909;
          }
        `;
      } else if (props.ui === "secondary") {
        return css`
          color: red;
          background-color: #fff;
          border-radius: 20px;
          border: 5px solid blue;
          &:hover {
            background-color: #fff;
            border: 5px solid red;
          }
        `;
      }
    }}
  `;

  return (
    <div
      style={{ border: "5px solid blue", padding: "5px", marginBottom: "5px" }}
    >
      <p style={{ textAlign: "center" }}>ui</p>
      <div
        style={{
          width: "100vw",
          display: "flex",
          justifyContent: "center",
          alignItems: "center",
          gap: "20px",
        }}
      >
        <FooButton ui={"primary"}>ui={"primary"}</FooButton>
        <FooButton ui={"secondary"}>ui={"secondary"}</FooButton>
      </div>
    </div>
  );
}

```

**ビジュアルで見ると下のようなイメージ**
![ui.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3337943/6bd2e22d-520e-1b90-45da-261fc813aa7c.gif)

## 変更したコードのみを抽出してみた

```typescript:hogeButton.tsx
// 変更前
if (props.variant === "text") {
        return css`
          color: red;
          background-color: #fff;
          border: 5px solid yellow;
          &:hover {
            background-color: #fff;
            border: 5px solid blue;
          }
        `;
        }

<HoggButton variant={"text"}>variant={"text"}</HoggButton>
```

```typescript:fooButton.tsx
// 変更後
if (props.ui === "primary") {
        return css`
          background-color: #fff;
          border: 5px solid blue;
          border-radius: 20px;
          &:hover {
            color: #909;
            border: 5px solid #909;
          }
        `;
      }

<FooButton ui={"primary"}>ui={"primary"}</FooButton>
```

## なんで変更したのか

今の現場では、主に利用している Button が 2 種類存在する。  
変更前までは、`variant`で`text`を渡しているものを**決定ボタン**などの主要ボタンとしており、`variant`で`outline`を渡しているものを**キャンセルボタン**としていた。

だが、同僚から`variant`で渡しているボタンの名前どうにかならないの？というレビューを受けた。

対策として`ui`という型を作り、`primary`と`secondary`という値によってボタンのスタイルが変更できるようにした。[^1]

## まとめ

MUI が既存で持っている props（variant）でのスタイル変更から、Button に新しく型を持たせてスタイルを変更する方法についてまとめた。

ちなみに初めてテックブログ的なものを書いたので、月 1 回くらいのペースで継続していきたいと思う。

## 参考文献

https://mui.com/material-ui/api/button/#props

[^1]: 主要で使うボタンだから、`primary`、その次に使うボタンだから`secondary`と安直に決めてしまったけど、良い命名とかあると教えて欲しい 🙇‍♂️