Для доступності елементи форм зв'язують через id: <label htmlFor>, aria-describedby, aria-labelledby. Але компонент поля може бути на сторінці кілька разів - жорстко прописаний id="email" дублюватиметься.
useId генерує унікальний ідентифікатор для кожного екземпляра компонента:
import { useId } from 'react';
function PasswordField({ label, hint }) {
const id = useId();
return (
<>
<label htmlFor={id}>{label}</label>
<input id={id} type="password" aria-describedby={`${id}-hint`} />
<p id={`${id}-hint`}>{hint}</p>
</>
);
}
Один виклик - базовий id, а пов'язані ідентифікатори утворюють з суфіксами.
Чому не Math.random() чи лічильник:
- серверний рендер: HTML генерується на сервері, потім React «гідратує» його в браузері.
Math.random()дасть на клієнті інший id, ніж на сервері, - помилка розбіжності гідратації, а зв'язкиlabel-inputзламаються; - кожен рендер - новий id, якщо генерувати в тілі компонента, і атрибути постійно змінюватимуться;
- глобальний лічильник залежить від порядку рендеру, який на сервері й клієнті може відрізнятися.
useId будує ідентифікатор зі шляху компонента в дереві - він однаковий на сервері й клієнті і стабільний між рендерами.
Чого useId не робить:
- не для
keyу списках - ключі мають походити з даних (item.id); - не для пошуку елементів через
document.getElementById- для доступу до DOM єuseRef; - не гарантує конкретного формату - не будуйте на ньому CSS-селектори.
Кілька застосунків React на одній сторінці (наприклад, «острівці» на серверній сторінці) можуть згенерувати однакові id. Для них задається префікс: createRoot(el, { identifierPrefix: 'search-' }) і такий самий на сервері.