useId

useId - хук React для генерації унікальних ідентифікаторів, які можна передавати атрибутам доступності.

const id = useId()

Довідник

useId()

Викличте useId на верхньому рівні вашого компонента, щоб згенерувати унікальний ID:

import { useId } from 'react';

function PasswordField() {
  const passwordHintId = useId();
  // ...

Дивіться більше прикладів нижче.

Параметри

useId не приймає жодних параметрів.

Повертає

useId повертає унікальний рядок ідентифікатора, пов'язаний з цим викликом useId у цьому компоненті.

Застереження

  • useId є хуком, тому ви можете викликати його лише на верхньому рівні вашого компонента або ваших власних хуків. Ви не можете викликати його всередині циклів або умов. Якщо вам це потрібно, витягніть новий компонент і перемістіть стан до нього.

  • useId не слід використовувати для генерації ключів у списку. Ключі слід генерувати з ваших даних.


Використання

Не викликайте useId для генерації ключів у списку. Ключі слід генерувати з ваших даних.

Генерування унікальних ідентифікаторів для атрибутів доступності

Викличте useId на верхньому рівні вашого компонента, щоб згенерувати унікальний ID:

import { useId } from 'react';

function PasswordField() {
  const passwordHintId = useId();
  // ...

Після цього можна передати згенерований ID</CodeStep> до різних атрибутів:</p> <pre><code data-meta="[[1, 2, "passwordHintId"], [1, 3, "passwordHintId"]]" class="language-js"><> <input type="password" aria-describedby={passwordHintId} /> <p id={passwordHintId}> </>

Давайте розглянемо приклад, щоб побачити, коли це може бути корисним.

Атрибути доступності HTML , такі як aria-describedby дозволяють вказати, що два теги пов'язані між собою. Наприклад, ви можете вказати, що елемент (наприклад, введення) описується іншим елементом (наприклад, абзацом).

У звичайному HTML ви б написали це так:

<label>
  Password:
  <input
    type="password"
    aria-describedby="password-hint"
  />
</label>
<p id="password-hint">
  The password should contain at least 18 characters
</p>

Втім, таке жорстке кодування ідентифікаторів не є гарною практикою в React. Компонент може відображатися більше одного разу на сторінці - але ідентифікатори повинні бути унікальними! Замість того, щоб жорстко кодувати ідентифікатор, згенеруйте унікальний ідентифікатор за допомогою useId:

import { useId } from 'react';

function PasswordField() {
  const passwordHintId = useId();
  return (
    <>
      <label>
        Password:
        <input
          type="password"
          aria-describedby={passwordHintId}
        />
      </label>
      <p id={passwordHintId}>
        The password should contain at least 18 characters
      </p>
    </>
  );
}

Тепер, навіть якщо PasswordField з'явиться на екрані кілька разів, згенеровані ідентифікатори не будуть конфліктувати.

import { useId } from 'react';

function PasswordField() {
  const passwordHintId = useId();
  return (
    <>
      <label>
        Password:
        <input
          type="password"
          aria-describedby={passwordHintId}
        />
      </label>
      <p id={passwordHintId}>
        The password should contain at least 18 characters
      </p>
    </>
  );
}

export default function App() {
  return (
    <>
      <h2>Choose password</h2>
      <PasswordField />
      <h2>Confirm password</h2>
      <PasswordField />
    </>
  );
}
input { margin: 5px; }

Подивіться це відео, щоб побачити різницю у користуванні допоміжними технологіями.

При серверному рендерингу, useId потрібне ідентичне дерево компонентів на серверній та клієнтській сторонах. Якщо дерева, які ви рендерите на сервері та клієнті, не збігаються, згенеровані ідентифікатори не співпадуть.

Чому useId кращий за лічильник з інкрементом?

Вам може бути цікаво, чому useId краще, ніж інкрементувати глобальну змінну на зразок nextId++.

Основна перевага useId полягає в тому, що React гарантує роботу з серверним рендерингом. Під час серверного рендерингу ваші компоненти генерують HTML-виведення. Пізніше, на клієнтській стороні, гідратація приєднує ваші обробники подій до згенерованого HTML. Щоб гідратація працювала, клієнтське виведення має відповідати серверному HTML.

Це дуже важко гарантувати за допомогою лічильника, що збільшується, оскільки порядок, в якому клієнтські компоненти гідратуються, може не відповідати порядку, в якому був виданий серверний HTML. Викликавши useId, ви гарантуєте, що гідратація спрацює, а виведення буде збігатися між сервером і клієнтом.

Всередині React, useId генерується з "батьківського шляху" викликаючого компонента. Ось чому, якщо дерево клієнта і сервера однакові, "батьківський шлях" буде збігатися незалежно від порядку рендерингу.


Якщо вам потрібно надати ідентифікатори кільком пов'язаним елементам, ви можете викликати useId, щоб згенерувати для них спільний префікс:

import { useId } from 'react';

export default function Form() {
  const id = useId();
  return (
    <form>
      <label htmlFor={id + '-firstName'}>First Name:</label>
      <input id={id + '-firstName'} type="text" />
      <hr />
      <label htmlFor={id + '-lastName'}>Last Name:</label>
      <input id={id + '-lastName'} type="text" />
    </form>
  );
}
input { margin: 5px; }

Це дозволяє уникнути виклику useId для кожного елемента, який потребує унікального ID.


Вказівка спільного префікса для всіх згенерованих ідентифікаторів

Якщо ви рендерите декілька незалежних React-застосунків на одній сторінці, передайте identifierPrefix як опцію до викликів createRoot або hydrateRoot. Це гарантує, що ідентифікатори, згенеровані двома різними програмами, ніколи не зіткнуться, оскільки кожен ідентифікатор, згенерований за допомогою useId, починатиметься з окремого префікса, який ви вказали.

<!DOCTYPE html>
<html>
  <head><title>My app</title></head>
  <body>
    <div id="root1"></div>
    <div id="root2"></div>
  </body>
</html>
import { useId } from 'react';

function PasswordField() {
  const passwordHintId = useId();
  console.log('Generated identifier:', passwordHintId)
  return (
    <>
      <label>
        Password:
        <input
          type="password"
          aria-describedby={passwordHintId}
        />
      </label>
      <p id={passwordHintId}>
        The password should contain at least 18 characters
      </p>
    </>
  );
}

export default function App() {
  return (
    <>
      <h2>Choose password</h2>
      <PasswordField />
    </>
  );
}
import { createRoot } from 'react-dom/client';
import App from './App.js';
import './styles.css';

const root1 = createRoot(document.getElementById('root1'), {
  identifierPrefix: 'my-first-app-'
});
root1.render(<App />);

const root2 = createRoot(document.getElementById('root2'), {
  identifierPrefix: 'my-second-app-'
});
root2.render(<App />);
#root1 {
  border: 5px solid blue;
  padding: 10px;
  margin: 5px;
}

#root2 {
  border: 5px solid green;
  padding: 10px;
  margin: 5px;
}

input { margin: 5px; }

Використання однакового префікса ідентифікатора на клієнтській стороні та сервері

Якщо ви рендерите декілька незалежних React-застосунків на одній сторінці, і деякі з цих застосунків рендеряться на сервері, переконайтеся, що identifierPrefix, який ви передаєте у виклик hydrateRoot на стороні клієнта, збігається з identifierPrefix, який ви передаєте до серверних API, таких як renderToPipeableStream.

// Server
import { renderToPipeableStream } from 'react-dom/server';

const { pipe } = renderToPipeableStream(
  <App />,
  { identifierPrefix: 'react-app1' }
);
// Client
import { hydrateRoot } from 'react-dom/client';

const domNode = document.getElementById('root');
const root = hydrateRoot(
  domNode,
  reactNode,
  { identifierPrefix: 'react-app1' }
);

Вам не потрібно передавати identifierPrefix якщо на сторінці лише один React-застосунок.