はじめに
みなさんは React 18から使えるようになった useId
を使いこなせていますか?
useId
は、フォームやコンポーネントにユニークなid属性を付けたいとき、とても便利なフックです。
でも実際にを使ってみると──
- 毎回suffixを手動で付けるのが面倒だったり
- 同じsuffix を重複して使ってしまったり
-
useId
を何度も呼ぶ必要があったり
と、ちょっとしたストレスを感じる場面もあります。
そこでこの記事では、useId
を活用しつつ、複数のidを一括で安全に生成できるCustom Hookを作ってみたので紹介します!
useId
とは?
まずは、useId
の基本からおさらいしましょう。
useId は、アクセシビリティ属性に渡すことができる一意のidを生成するための React フックです。
引用:useId – React
このように、useId
を使うことで、コンポーネント間で衝突しないユニークなidを簡単に生成できます。
複数のidを指定したいとき
では、複数の要素に ID をつけたい場合、どうしているでしょうか?
たとえば次のような方法が一般的です。
import { useId } from 'react'
// suffixを付ける方法
const id = useId();
const nameId = `${id}-name`;
const emailId = `${id}-email`;
// useIdを複数回使う方法
const nameId = useId();
const emailId = useId();
ただ、これらの方法には次のような課題があります。
-
1つずつ定義を書く必要がある
- ファイルの行数が増え、手間もかかる
-
suffixを手書きしているため、重複やタイプミスの可能性がある
- 実行時に気付きにくいバグにつながる
複数idを同時に定義するCustom Hook「useIds」
こんなふうに書けたら便利では?
これらの課題を解決して、
こんなふうに一括で ID を定義できたら嬉しいですよね。
const { nameId, emailId } = useIds('name', 'email');
// nameId → ${id}-name
// emailId → ${id}-email
-
nameId
,emailId
という変数名でidが生成される - 中身は
useId
に基づいたユニークなidになる
しかも、suffixの重複やタイプミスも型でチェックできるとしたら便利ですよね
Custom Hook「useIds」 実装
ということで、こんな感じのuseIdsいうCustom Hookを作ってみました。
import { useId } from 'react';
// 重複チェック用の型
type HasDuplicates<T extends readonly any[]> = T extends readonly [infer First, ...infer Rest]
? First extends Rest[number]
? true
: HasDuplicates<Rest>
: false;
// 重複がある場合はnever型を返す
type NoDuplicates<T extends readonly string[]> = HasDuplicates<T> extends true
? never
: T;
type IdsObject<IDS extends readonly string[]> = {
[K in `${IDS[number]}Id`]: string;
};
export const useIds = <T extends readonly string[]>(...suffixes: NoDuplicates<T>) => {
const baseId = useId();
return suffixes.reduce((acc, suffix) => ({ ...acc, [`${suffix}Id`]: `${baseId}-${suffix}`}),{} as IdsObject<T>)
};
○ HasDuplicates<T>
の仕組み
HasDuplicates<T>
は、引数に渡した値が重複しているかチェックしています。
仕組みとしては、こんな感じです。
- 配列を分解する
-
infer
で先頭要素をFirst
、残りをRest
に取りだす - 要素ゼロだったら、falseを返す
-
- 重複チェック
-
Rest[number]
(残りの配列)にFirst
と同じ要素が含まれているか調べる
-
- 重複あり
- 重複した要素があれば、trueを返す
- 重複なし
-
Rest
を新しい入力として、HasDuplicates
に渡し、再度重複をチェックする
-
○ NoDuplicates<T>
の仕組み
NoDuplicates<T>
は、HasDuplicates<T>
が true があったら、型エラーにしています。
-
HasDuplicates<T>
がtrue
なら-
never
を返す
-
-
HasDuplicates<T>
がfalse
なら- 引数に渡した値を返す
○ IdsObject<IDS>
の仕組み
IdsObject<IDS>
は、引数に渡した値からプロパティ名を動的に生成する組み立てるものです。
具体的には、引数で受け取った値を ${IDS[number]}Id
というキーで展開して、stringの型として定義しています。
まとめ
この記事では、複数idを同時に定義できる Custom Hook「useIds」を作ってみたので紹介しました。
ぜひこの記事を参考に、useIdsをつかって、idを生成して、アクセシブルなWebサイトを作っていきましょう!
最後まで読んでくださってありがとうございます!
普段はデザインやフロントエンドを中心にQiitaに記事を投稿しているので、ぜひQiitaのフォローとX(Twitter)のフォローをお願いします。