Что такое useId в React?
Зачем нужен useId
useId — хук, появившийся в React 18, который генерирует уникальную стабильную строку-идентификатор для использования в атрибутах разметки: id, aria-describedby, aria-labelledby и подобных. Главное отличие от простого счётчика или Math.random() — безопасность при серверном рендеринге (SSR) и последующей гидратации на клиенте.
Проблема, которую решает
До появления useId разработчики генерировали идентификаторы вручную — инкрементным счётчиком, Math.random() или библиотекой uuid. При SSR это приводило к тому, что id, сгенерированный на сервере, не совпадал с id, сгенерированным при повторном рендере на клиенте во время гидратации. React выдавал предупреждения о hydration mismatch, а связка label/input через htmlFor могла сломаться, что критично для доступности (a11y).
useId учитывает позицию компонента в дереве рендеринга, поэтому одно и то же дерево на сервере и клиенте всегда порождает одинаковые id.
Базовое использование
function Field() {
const id = useId();
return (
<>
<label htmlFor={id}>Имя</label>
<input id={id} type="text" />
</>
);
}
Несколько связанных id в одном компоненте
Если внутри компонента нужно несколько разных id, не стоит вызывать useId многократно — правильный подход - вызвать хук один раз и добавлять суффиксы:
function Form() {
const id = useId();
return (
<form>
<label htmlFor={`${id}-name`}>Имя</label>
<input id={`${id}-name`} />
<label htmlFor={`${id}-email`}>Email</label>
<input id={`${id}-email`} />
</form>
);
}
Важные нюансы
- useId — не замена key. Сгенерированный id не подходит в качестве
keyдля элементов списка: он одинаков для всех экземпляров при одинаковой структуре рендера и не связан с данными элемента. - Один вызов на компонент. Хук нельзя вызывать в цикле или условно — это нарушает правила хуков. Для списка полей id формируются суффиксами от одного базового значения либо через компонент-обёртку, вызывающий
useIdна каждый элемент отдельно. - Формат непредсказуем. Возвращаемая строка выглядит как
:r0:или:r1:— не стоит полагаться на её вид или пытаться парсить. - Несколько React-приложений на странице. Если на одной странице рендерится несколько независимых React-деревьев (несколько вызовов
createRootилиrenderToString), между ними возможны коллизии id. Для этого уhydrateRoot/createRootи серверного рендеринга есть опцияidentifierPrefix, задающая уникальный префикс для каждого дерева.
Когда применять
- Связка
labelиinputчерезhtmlFor/id. - Атрибуты доступности:
aria-describedby,aria-labelledby,aria-controls. - Любые другие DOM-атрибуты, где нужен уникальный, но детерминированный на сервере и клиенте идентификатор.
Что хочет услышать интервьюер
Понимание, что useId решает проблему hydration mismatch при SSR
Знание, что useId нельзя использовать как key в списках
Пример связки label/htmlFor с useId
Понимание паттерна с суффиксами для нескольких id в одном компоненте
Знание про identifierPrefix для нескольких независимых React-корней на странице
Пример: Базовое использование с label
function Field() {
const id = useId();
return (
<>
<label htmlFor={id}>Имя</label>
<input id={id} type="text" />
</>
);
}
Пример: Несколько id в одном компоненте через суффиксы
function Form() {
const id = useId();
return (
<form>
<label htmlFor={`${id}-name`}>Имя</label>
<input id={`${id}-name`} />
<label htmlFor={`${id}-email`}>Email</label>
<input id={`${id}-email`} type="email" />
</form>
);
}
Типичные ошибки
Использование useId для генерации key в map() по списку
Вызов useId в цикле или внутри условия вместо одного вызова на верхнем уровне компонента
Непонимание, зачем вообще нужен useId, если можно использовать Math.random() или uuid
Ожидание, что возвращаемая строка будет коротким читаемым числом, а не служебным форматом вроде :r0:
Незнание про identifierPrefix при работе с несколькими React-приложениями на одной странице


