useId je React Hook za generisanje jedinstvenih ID-eva koji se mogu proslediti atributima pristupačnosti.

const id = useId()

Reference

useId()

Pozovite useId na vrhu vaše komponente da biste generisali jedinstveni ID:

import { useId } from 'react';

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

Pogledajte još primera ispod.

Parametri

useId ne prima nikakve parametre.

Povratne vrednosti

useId vraća jedinstveni ID string asociran za specifični useId poziv u specifičnoj komponenti.

Upozorenja


Upotreba

Pitfall

Nemojte pozivati useId da biste generisali ključeve u listi. Ključevi trebaju biti generisani na osnovu vaših podataka.

Generisanje jedinstvenih ID-eva za atribute pristupačnosti

Pozovite useId na vrhu vaše komponente da biste generisali jedinstveni ID:

import { useId } from 'react';

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

Onda možete proslediti generisani ID u različite atribute:

<>
<input type="password" aria-describedby={passwordHintId} />
<p id={passwordHintId}>
</>

Prođimo kroz primer da vidimo kada je ovo korisno.

HTML atributi pristupačnosti kao što je aria-describedby vam omogućavaju da specificirate dva tag-a koja su povezana. Na primer, možete specificirati da je element (kao što je input) opisan drugim elementom (kao što je paragraph).

U običnom HTML-u napisali biste ovako nešto:

<label>
Šifra:
<input
type="password"
aria-describedby="password-hint"
/>
</label>
<p id="password-hint">
Šifra treba da sadrži barem 18 karaktera
</p>

Međutim, hardkodirani ID-evi poput ovih nisu dobra praksa u React-u. Komponenta može biti renderovana više od jednom na stranici—ali ID-evi moraju biti jedinstveni! Umesto da hardkodirate ID, generišite jedinstveni ID sa useId:

import { useId } from 'react';

function PasswordField() {
const passwordHintId = useId();
return (
<>
<label>
Šifra:
<input
type="password"
aria-describedby={passwordHintId}
/>
</label>
<p id={passwordHintId}>
Šifra treba da sadrži barem 18 karaktera
</p>
</>
);
}

Sada, čak iako se PasswordField pojavi više puta na ekranu, generisani ID-evi se neće preklapati.

import { useId } from 'react';

function PasswordField() {
  const passwordHintId = useId();
  return (
    <>
      <label>
        Šifra:
        <input
          type="password"
          aria-describedby={passwordHintId}
        />
      </label>
      <p id={passwordHintId}>
        Šifra treba da sadrži barem 18 karaktera
      </p>
    </>
  );
}

export default function App() {
  return (
    <>
      <h2>Unesi šifru</h2>
      <PasswordField />
      <h2>Potvrdi šifru</h2>
      <PasswordField />
    </>
  );
}

Pogledajte ovaj video da biste uočili razliku u korisničkom iskustvu sa pomoćnim tehnologijama.

Pitfall

Sa renderovanjem na serveru, useId zahteva identično stablo komponente na serveru i na klijentu. Ako se stabla koja renderujete na serveru i klijentu ne poklapaju, ni generisani ID-evi se neće poklapati.

Deep Dive

Zašto je useId bolji od inkrementalnog brojača?

Možda se pitate zašto je useId bolji od inkrementiranja globalne promenljive poput nextId++.

Primarna korist od useId je da React garantuje da radi sa renderovanjem na serveru. Tokom renderovanja na serveru, vaše komponente generišu HTML izlaz. Kasnije, na klijentu, hidratacijom se povezuju vaši event handler-i sa generisanim HTML-om. Da bi hidratacija radila, klijentski izlaz se mora poklapati sa serverskim HTML-om.

Ovo je teško garantovati sa inkrementalnim brojačem jer se redosled u kom se Client Components hidratizuju možda neće poklapati sa redosledom u kom je emitovan HTML sa servera. Pozivanjem useId osiguravate da će hidratacija raditi i da će se izlaz servera poklapati sa klijentskim.

Unutar React-a, useId je generisan na osnovu “roditeljske putanje” pozivajuće komponente. Zbog toga, ako se klijentsko i serversko stablo poklapaju, “roditeljska putanja” će se poklapati bez obzira na redosled.


Ako vam je potrebno da napravite ID-eve za više povezanih elemenata, možete pozvati useId da generišete deljeni prefiks za njih:

import { useId } from 'react';

export default function Form() {
  const id = useId();
  return (
    <form>
      <label htmlFor={id + '-firstName'}>Ime:</label>
      <input id={id + '-firstName'} type="text" />
      <hr />
      <label htmlFor={id + '-lastName'}>Prezime:</label>
      <input id={id + '-lastName'} type="text" />
    </form>
  );
}

Ovo vam omogućava da izbegnete pozivanje useId-a za svaki pojedinačni element kojem je potreban jedinstveni ID.


Specificiranje deljenog prefiksa za sve generisane ID-eve

Ako renderujete više nezavisnih React aplikacija na jednog stranici, prosledite identifierPrefix u vaše createRoot ili hydrateRoot pozive. Ovo osigurava da se ID-evi generisani u dve različite aplikacije nikad neće preklapati jer će svaki identifikator generisan sa useId početi sa različitim prefiksom koji ste specificirali.

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 />);


Upotreba istog ID prefiksa na klijentu i serveru

Ako renderujete više nezavisnih React aplikacija na istoj stranici, a neke od tih aplikacija su renderovane na serveru, postarajte se da identifierPrefix koji prosleđujete u hydrateRoot poziv na klijentskoj strani bude isti kao i identifierPrefix koji prosleđujete u serverske API-je poput renderToPipeableStream.

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

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

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

Nije potrebno da prosleđujete identifierPrefix ako imate samo jednu React aplikaciju na stranici.