万能なコンポーネントを作りたい

万能なコンポーネントを作りたい

投稿日: 2025年01月06日

学習振り返り
要約
  • 汎用的なボタンコンポーネントを作成し、複数のスタイルをvariantで設定できるようにした。
  • ComponentPropsWithRefやOmitを使用して、buttonタグの属性を柔軟に扱いつつ特定の属性を除去した。
  • forwardRefを使い、親コンポーネントから子コンポーネントにrefを渡すことで、操作性を向上させた。

はじめに

オリアプ第一弾でコードレビューしていただく中でコンポーネントの汎用性を上げる型をたくさん教えていただいて、いろんなところで応用できるなと思ったので、Qiitaで記事を書いていたのですが、こちらでも共有しておいた方がいいかなと思ったので大事なところだけ抜粋してまとめてみます。

コンポーネント

Buttonのコンポーネントです。
基本デザインは複数パターンがあるけど、いろんなPropsを受け取れるようにするための手法です。

import { ReactNode, ComponentPropsWithRef, forwardRef } from "react";

type Variant =
  | "outlined"
  | "contained-blu"
  | "contained-gry"
  | "contained-blu500";

interface Props extends Omit<ComponentPropsWithRef<"button">, "className"> {
  variant?: Variant;
  children?: ReactNode;
}

export const Button = forwardRef<HTMLButtonElement, Props>(
  ({ variant, children, ...props }, ref) => {
    const className = () => {
      switch (variant) {
        case "outlined":
          return "border-solid border-2 border-slate-600";
        case "contained-blu":
          return "bg-custom-blue";
        case "contained-gry":
          return "bg-gray-300";
        case "contained-blu500":
          return "bg-blue-500 text-white";
        default:
          return "";
      }
    };
    return (
      <button
        ref={ref}
        {...props}
        className={`w-full h-full rounded-full ${className()}`}
      >
        {children}
      </button>
    );
  }
);

Button.displayName = "Button";

詳しくみる

ComponentPropsWithRef

下記のように記述するとbuttonタグが持つすべての属性をpropsにすることができる型です。

ComponentPropsWithRef<"button">

refを許容したかったのでComponentPropsWithRefを使いましたが、許容したくない場合はComponentPropsWithoutRefを使います。
使う属性だけ列挙していかなくてもこれだけでまるっと指定できるので便利です。


Omit

特定の属性だけを除去できる型です。
今回はスタイルを統一するためにコンポーネントをつくったので、classNameを渡されては困ります。ということでclassNameを除去しました。

Omit<ComponentPropsWithRef<"button">, "className">

背景色はpropsでvariantを用意して変えられるようにしています。
今回はパターン4つでvariantで指定するため、Omitで対応しました。
より汎用性を上げて、スタイルも親コンポーネントで自由に操作できるようにしようと考えたらpropsにclassNameを用意しないと不具合生じることがあるようです。

forwardRef

親コンポーネントから子コンポーネントにrefを渡すための仕組みです。
refを渡せるようにしていなかったら(forwardRefを使わなかったら)子コンポーネントのインスタンスに直接アクセスできないので、例えばフォーカスの設定やアニメーションの制御、フォームバリデーション等を別途状態管理などを行って操作しないといけないということになります。

おわりに

ボタンのコンポーネントっていくつかパターンがあったり、少しだけ変えたいみたいなこともあるので汎用性が上がるとより便利にコンポーネントが扱えると思います。
そのため改めてShiftBブログでも取り上げてみました。
参考になれば嬉しいです。

シェア!

Threads
user
吉本茜
山口在住/二児の母/育休中
Loading...
記事一覧に戻る
Threads
0