11
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

みなさんは 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を作ってみました。

useIds.tsx
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> は、引数に渡した値が重複しているかチェックしています。
仕組みとしては、こんな感じです。

  1. 配列を分解する
    • infer で先頭要素を First、残りを Rest に取りだす
    • 要素ゼロだったら、falseを返す
  2. 重複チェック
    • Rest[number](残りの配列)に First と同じ要素が含まれているか調べる
  3. 重複あり
    • 重複した要素があれば、trueを返す
  4. 重複なし
    • 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)のフォローをお願いします。

11
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?