useId zwraca unikalne id dla każdej instancji komponentu i to samo id przy każdym renderowaniu. Używaj go, żeby połączyć <label> z jego <input> albo pole z jego podpowiedzią przez aria-describedby, gdy komponent może pojawić się na stronie więcej niż raz.
Ten sam komponent renderuje się dwa razy i dostaje dwa różne id, wypisane pod każdym polem. Kliknij tekst "Confirm password", a fokus przeskoczy na drugie pole, bo jego htmlFor pasuje tylko do id tego pola. Wpisz na sztywno id="password" zamiast useId(), a obie etykiety będą wskazywać na pierwsze pole.
Składnia
const id = useId();
useId nie przyjmuje argumentów i zwraca tekst. Wywołuj go na najwyższym poziomie komponentu, jak każdy hook. Jego dokładny format jest wewnętrzny i zmieniał się między wersjami: React 18 tworzył :r1:, a React 19.2 tworzy _r_1_ dla komponentu wyrenderowanego najpierw w przeglądarce i id zaczynające się od _R_, zbudowane z pozycji w drzewie, dla komponentu wyrenderowanego na serwerze. Nigdy go nie parsuj i nie polegaj na jego kształcie.
Dlaczego nie Math.random ani licznik
Id dla dostępności muszą się zgadzać między HTML, który wysyła serwer, a drzewem, które React buduje w przeglądarce. Oba oczywiste sposoby tworzenia id nie przechodzą tego testu.
// Changes on every render, and differs between server and browser
const id = 'field-' + Math.random().toString(36).slice(2);
// The server's counter keeps growing across requests, the browser starts at 0
let nextId = 0;
const id = 'field-' + nextId++;
Przy renderowaniu na serwerze (Next.js, tryb frameworka React Router, dowolna konfiguracja z hydrateRoot) serwer wypisuje do HTML id="field-4817", pierwsze renderowanie w przeglądarce wylicza field-0, a React zgłasza niezgodność hydracji. useId buduje id z pozycji komponentu w drzewie, która jest identyczna po obu stronach.
Nawet bez serwera id zbudowane podczas renderowania może zmieniać się przy każdym renderowaniu. Ten przykład pokazuje różnicę bez żadnego serwera:
Kliknij kilka razy przycisk. Wartość z useId zostaje na miejscu, a id z licznika rośnie przy każdym renderowaniu, więc wszystko, co wskazywało na stare id (aria-describedby, etykieta), wskazuje teraz na nic. Owinięcie licznika w useState(() => nextId++) naprawiłoby ponowne renderowania, ale nie niezgodność z serwerem.
Kilka id z jednego wywołania
Komponent z kilkoma polami nie potrzebuje kilku wywołań useId. Wygeneruj jedną podstawę i dodaj przyrostek dla każdego elementu.
Wpisz w polu email słowo bez @: pojawi się błąd, a aria-describedby pola wskaże na niego, więc czytnik ekranu przeczyta błąd, gdy pole dostanie fokus. Wyrenderuj <SignupForm /> dwa razy w App, a każda kopia dostanie własne bazowe id.
Nie do kluczy listy
Klucze i id rozwiązują różne problemy. Klucz mówi Reactowi, który element jest którym między renderowaniami, więc musi pochodzić z danych. useId daje jedno id na instancję komponentu i nie możesz go wywołać wewnątrz map.
// Wrong: breaks the rules of hooks, and the key is unrelated to the item
{todos.map((todo) => <Todo key={useId()} todo={todo} />)}
// Right: the key comes from the data
{todos.map((todo) => <Todo key={todo.id} todo={todo} />)}
Gdy twoje dane nie mają id, utwórz je przy tworzeniu elementu (crypto.randomUUID() w handlerze zdarzenia, który go dodaje), a nie podczas renderowania. Strona o listach i kluczach wyjaśnia, dlaczego klucz musi zostać przy swoim elemencie.
Kilka rootów Reacta na jednej stronie
Jeśli na tej samej stronie renderują się dwie osobne aplikacje React, ich id mogłyby kolidować. Nadaj każdemu rootowi przedrostek:
createRoot(document.getElementById('cart'), { identifierPrefix: 'cart-' });
createRoot(document.getElementById('chat'), { identifierPrefix: 'chat-' });
Przy renderowaniu na serwerze przekaż to samo identifierPrefix do renderera serwerowego i do hydrateRoot, żeby obie strony tworzyły te same id.
Częste błędy
Renderowanie innego drzewa na serwerze i w przeglądarce. useId zależy od pozycji komponentu, więc gałąź taka jak typeof window === 'undefined' ? <A /> : <B /> nad polem może przesunąć id między dwoma renderowaniami. Podczas hydracji utrzymuj to samo drzewo i przełączaj je dopiero po niej, w efekcie.
Wyszukiwanie elementu po jego id. document.getElementById(id) działa, ale ref to sposób Reacta na dotarcie do węzła DOM i w ogóle nie potrzebuje id.
Używanie go jako losowej wartości. Id jest unikalne w obrębie aplikacji, ale nie jest losowe i da się je przewidzieć z drzewa. Nie używaj go do tokenów bezpieczeństwa, kluczy cache ani niczego, co jest przechowywane między sesjami.
Kiedy go używać
Używaj useId zawsze, gdy komponent wielokrotnego użytku potrzebuje atrybutu id: pola formularza zbudowane z komponentu używanego wiele razy, tooltip połączony przez aria-describedby, okno dialogowe z aria-labelledby, karty z aria-controls. Gdy id łączy tylko etykietę z polem, możesz też pominąć id i zagnieździć pole w etykiecie (<label>Name <input /></label>); używaj useId, gdy elementów nie da się zagnieździć.
Najczęściej zadawane pytania
Co robi useId w React?
Zwraca tekst, który jest unikalny dla danej instancji komponentu i pozostaje taki sam przy każdym renderowaniu. Używasz go do łączenia elementów przez id: htmlFor na etykiecie, aria-describedby na polu, aria-labelledby na oknie dialogowym.
Dlaczego nie używać Math.random() albo licznika do id?
Dają różne wartości na serwerze i w przeglądarce, więc strona wyrenderowana na serwerze i jej wersja po hydracji się nie zgadzają, a React zgłasza niezgodność hydracji. Math.random() zmienia się też przy każdym renderowaniu. useId wyprowadza id z pozycji komponentu w drzewie, która jest taka sama w obu miejscach.
Czy mogę używać useId jako kluczy w liście?
Nie. Klucz musi pochodzić z twoich danych, żeby React mógł dopasować ten sam element między renderowaniami. useId jest wywoływany raz na komponent, a wywołanie go wewnątrz map i tak łamie zasady hooków. Użyj własnego id elementu.
Jak dostać kilka id z jednego wywołania useId?
Wywołaj useId raz i dodaj przyrostki: ${id}-name, ${id}-email. Podstawa jest unikalna, więc teksty z przyrostkami też.
Czy mogę użyć id z useId w selektorze CSS?
Unikaj tego. Id służy do łączenia elementów w DOM, a jego dokładny format to wewnętrzny szczegół, który zmieniał się między wersjami Reacta. Styluj przez klasę, a elementy wyszukuj przez ref zamiast querySelector.