Polymorphic React Components
Componentの中のHTMLタグを多相にする
<Button as="a" />
とすれば <button>
ではなく <a>
になる感じ
要件
指定されたタグのみ選択できる
e.g. "a" | "button"
指定されたタグの標準のpropsのみ受け入れる
e.g. "a"
を指定した場合は、 href=
が使える
e.g. "button"
を指定した場合は、 href=
が使えない
ユースケース
そんなに頑張らなくても、これで十分な気がしてきた
tstype AProps = {
as: 'a';
href: string;
};
type ButtonProps = {
as: 'button';
onClick: () => void;
};
type Props = AProps | ButtonProps;
export const AddButton: React.FC<Props> = ({ as: Tag, ...props }) => {
return (
<Tag
className="bg-gray-400 hover:bg-gray-500 text-white font-bold py-2 px-4 rounded-full"
{...props}
>
+
</Tag>
);
};
この記事、結論おかしくないか

この定義だと、 <Button tagName="button" href="..."/>
もvalidになってしまう
要素の制限ができない
任意のタグを指定できてしまう
tsexport const ButtonDefaultAsType = 'button' as const
export type ButtonDefaultAsType = typeof ButtonDefaultAsType
export type ButtonOwnProps<E extends React.ElementType> = {
children: React.ReactNode
as?: E
}
export type ButtonProps<E extends React.ElementType> = ButtonOwnProps<E> &
Omit<React.ComponentProps<E>, keyof ButtonOwnProps<E>>
export const Button = <E extends React.ElementType = ButtonDefaultAsType>({
children,
as,
...otherProps
}: ButtonProps<E>) => {
const Tag = as || ButtonDefaultAsType
return <Tag {...otherProps}>{children}</Tag>
}