はじめに
こんにちは。株式会社Another worksのnonoyamalfoyです。
先日、ドロップダウンコンポーネントを作成した際にGenericsを活用して汎用的なドロップダウンコンポーネントを作成したので共有します。
ドロップダウンformで持ちたいvalueの型は様々かと思います。
stringで持ちたい場合はもちろん、個数選択ならnumber, Objectの場合も想定されます。
しかし、anyはつかいたくない。そんなときにGenericsを使うことでのformで持ちたいvalueの型を利用側から指定できるようにします。
実際にコードを見てみる
コンポーネント側
// 選択肢の型
type Item<Value> = {
imageUrl?: string;
text: string;
value: Value | undefined;
};
type Props<Value> = {
...
// 選択中のItemの値
selectedValue: Value;
// 選択肢の配列
items: Item<Value>[];
// 選択肢をクリックしたときのイベントハンドラ
onClickSelectorItem?: (item: Item<Value>) => void
}
export function Dropdown<Value>({
...
selectedValue,
items,
onClickSelectorItem
}: Props<Value>) {
return (
...
)
}
呼び出し側で型を指定できる
const [selectedValue, setSelectedValue] = useState<string>("text1")
const items = [
{
text: 'text1',
value: 'text1',
},
{
text: 'text2',
value: 'text2',
},
]
...
// genericsで型を指定
<Dropdown<string>
...
// selectedItemValueがgenericsで指定した型になる
selectedItemValue={selectedItemValue}
// valueがgenericsで指定した型になる
items={items}
// 引数のvalueがgenericsで指定した型になる
onClickSelectorItem={onClickSelectorItem}
/>
このようにGenericsを使用することで、あらゆるvalueの型に対応し、汎用性の高いコンポーネントを実現できます。
補足
以下のように厳格な比較をしている場合
valueの型にObject型を指定したい場合にはitemsに渡すvalueとselectedValueの参照先を同じにしないといけないので注意。
// 選択中のitemを判断するロジック
const selectedItem = useMemo(
() => items.find(({ value }) => value === selectedItemValue),
[items, selectedItemValue],
);
MUIのSelectコンポーネントでもvalueにObjectを使うときは注意しろという文言がありました。
値がオブジェクトの場合、選択されるためには、そのオプションと参照一致しなければならない。値がオブジェクトでない場合、選択されるためには、その文字列表現はオプションの文字列表現と一致しなければならない
Share this post