~/gtn74

$ cat posts/20240921-tq2ou8.mdx

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

2024-09-21 · #tech #typescript #mui

はじめに

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

  • 変更前:MUI の Button がデフォで持っていた variant によって Button のスタイルを変更させる方法
  • 変更後:新たに Button に ui という型を持たせて Button のスタイルを変更させる方法 ButtonComponent.png

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

前提知識

変更点を説明する前の前提知識として、MUI の Button はvariantというpropsを受け取れる。variant で受け取った値によって下記のように変更できる。 standard MUI Button.png

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

変更前

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

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

変更後

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

  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

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

// 変更前
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>
// 変更後
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 種類存在する。
変更前までは、varianttextを渡しているものを決定ボタンなどの主要ボタンとしており、variantoutlineを渡しているものをキャンセルボタンとしていた。

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

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

まとめ

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

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

参考文献

Button API - Material UIAPI reference docs for the React Button component. Learn about the props, CSS, and other APIs of this exported module.mui.com

Footnotes

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

$