💡
ReactでComponentのPropsの型をどうするのがいいのか
初学者のReactのコンポーネント作成の課題
Reactを使うにあたってTypeScriptが普通になりつつある昨今ですが,ButtonやInputなどの粒度の大きいコンポーネントを作成するときにこんな感じにPropsが膨らんでしまっていることはありませんか?
import { ChangeEvent, VFC } from 'react';
type Props = {
className?: string;
id?: string;
name: string;
type?: string;
placeholder?: string;
defaultValue?: string;
value: string;
disabled?: boolean;
onChangeText: (
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => void;
}
export const Input: VFC<Props> = ({
className,
id,
name,
type,
placeholder,
defaultValue,
value,
disabled,
onChangeText,
}) => (
<input
className={className}
id={id}
name={name}
type={type}
placeholder={placeholder}
defaultValue={defaultValue}
value={value}
disabled={disabled}
onChange={(e): void => {
onChangeText(e);
}}
/>
);
🙉🙉🙉🙉🙉🙉🙉🙉🙉🙉🙉🙉🙉🙉
そんなときに,ちょっとReact,TypeScriptの中級者のとても簡単に使える知識を紹介します.
このようにPropsが多くなってしまう原因は,必要となるであろうHTML要素の属性の型を一つ一つ定義していることにあります.
例えばButtonコンポーネントの場合,まず,label
(または,children
)とonClick
が必要なので追加し,そういえばdisable
も必要なので足して,あとtype
もいるかーっと言った具合です.更にそこに属性以外のPropsも加わることでしょう.
解決法:各HTMLElementの属性の型があるので,それを使う
JSX.IntrinsicElements
もしくは[要素名]HTMLAttributes
を使うことで,Propsに属性を型定義する必要がなくなります.
例えば,先程のInputコンポーネントはこのように書くことができます.
import { VFC } from 'react';
type Props = JSX.IntrinsicElements['input'] & {
className?: string;
}
export const Input: VFC<Props> = ({ className, ...rest }) => {
return <input className={className} {...rest} />;
};
😇😇😇😇😇😇😇😇😇😇😇😇😇😇😇
幸せになれましたでしょうか?
また,JSX.IntrinsicElements['input']
をInputHTMLAttributes<HTMLInputElement>
に代えても問題ありませんので,そこらへんはお好みで.
私が使っているButtonコンポーネントの例も貼っておきます.(TailWindCSSを使っている点はご容赦ください)
import clsx from 'clsx';
import { VFC } from 'react';
type Props = JSX.IntrinsicElements['button'] & {
className?: string;
label: string;
outline?: boolean;
rounded?: boolean;
};
export const Button: VFC<Props> = ({
className,
label,
outline,
rounded,
...rest
}) => {
const defaultClassName = 'text-white bg-primary2';
const outlineClassName = 'text-primary2';
return (
<button
className={clsx(
className,
'cursor-pointer rounded border-2 border-primary2 py-1 px-4 font-bold',
outline ? outlineClassName : defaultClassName,
rounded && 'rounded-full',
)}
{...rest}
>
{label}
</button>
);
};
参考:
Discussion