Menu

L'hook useId di React: id univoci per label e ARIA

useId restituisce un id univoco e stabile per ogni istanza di un componente, così una label, un input e un suggerimento aria-describedby possono puntarsi a vicenda anche quando il componente compare molte volte. Scopri perché Math.random e i contatori si rompono con il rendering lato server, e perché useId non serve per le key delle liste.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

useId restituisce un id univoco per ogni istanza di un componente, e lo stesso id a ogni rendering. Usalo per collegare una <label> al suo <input>, o un input al suo suggerimento tramite aria-describedby, quando il componente può comparire più di una volta in una pagina.

Lo stesso componente viene renderizzato due volte e riceve due id diversi, stampati sotto ogni campo. Clicca il testo "Confirm password" e il focus salta al secondo input, perché il suo htmlFor corrisponde solo all'id di quell'input. Scrivi direttamente id="password" al posto di useId() ed entrambe le label puntano al primo input.

La sintassi

const id = useId();

useId non accetta argomenti e restituisce una stringa. Chiamalo al livello più alto del componente, come ogni hook. Il suo formato esatto è interno ed è cambiato tra una versione e l'altra: React 18 produceva :r1:, e React 19.2 produce _r_1_ per un componente renderizzato per la prima volta nel browser e un id che inizia con _R_, costruito dalla posizione nell'albero, per uno renderizzato sul server. Non analizzarlo mai e non fare affidamento sulla sua forma.

Perché non Math.random o un contatore

Gli id per l'accessibilità devono coincidere tra l'HTML che il server invia e l'albero che React costruisce nel browser. I due modi ovvi di creare un id falliscono entrambi questa prova.

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

Con il rendering lato server (Next.js, la modalità framework di React Router, qualsiasi configurazione con hydrateRoot), il server stampa id="field-4817" nell'HTML, il primo rendering del browser calcola field-0, e React segnala un hydration mismatch. useId costruisce l'id dalla posizione del componente nell'albero, che è identica da entrambe le parti.

Anche senza un server, un id costruito durante il rendering può cambiare a ogni rendering. Questo esempio mostra la differenza senza alcun server coinvolto:

Clicca il pulsante qualche volta. Il valore di useId resta fermo mentre l'id del contatore sale a ogni rendering, quindi qualsiasi cosa puntasse al vecchio id (un aria-describedby, una label) ora non punta a nulla. Racchiudere il contatore in useState(() => nextId++) risolverebbe i nuovi render ma non la discrepanza con il server.

Più id da una sola chiamata

Un componente con più campi non ha bisogno di più chiamate a useId. Genera una sola base e aggiungi un suffisso per ogni elemento.

Scrivi una parola senza @ nel campo email: compare l'errore e l'aria-describedby dell'input punta a esso, così uno screen reader legge l'errore quando l'input riceve il focus. Renderizza <SignupForm /> due volte in App e ogni copia riceve il proprio id di base.

Non per le key delle liste

Key e id risolvono problemi diversi. Una key dice a React quale voce è quale tra un rendering e l'altro, quindi deve arrivare dai dati. useId dà un id per ogni istanza di componente, e non puoi chiamarlo dentro 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} />)}

Quando i tuoi dati non hanno un id, creane uno quando la voce viene creata (crypto.randomUUID() nel gestore di eventi che la aggiunge), non durante il rendering. La pagina su liste e key spiega perché la key deve restare con la sua voce.

Più root React in una pagina

Se due app React separate vengono renderizzate nella stessa pagina, i loro id potrebbero entrare in conflitto. Dai a ogni root un prefisso:

createRoot(document.getElementById('cart'), { identifierPrefix: 'cart-' });
createRoot(document.getElementById('chat'), { identifierPrefix: 'chat-' });

Con il rendering lato server, passa lo stesso identifierPrefix al renderer del server e a hydrateRoot così entrambe le parti producono gli stessi id.

Errori comuni

Renderizzare un albero diverso sul server e nel browser. useId dipende dalla posizione del componente, quindi un ramo come typeof window === 'undefined' ? <A /> : <B /> sopra un campo può spostare gli id tra i due rendering. Mantieni l'albero uguale durante l'hydration e cambialo dopo, in un effetto.

Cercare l'elemento tramite il suo id. document.getElementById(id) funziona, ma un ref è il modo React per raggiungere un nodo DOM e non ha bisogno di alcun id.

Usarlo come valore casuale. L'id è univoco all'interno dell'app, non casuale, ed è prevedibile a partire dall'albero. Non usarlo per token di sicurezza, chiavi di cache o qualsiasi cosa memorizzata tra una sessione e l'altra.

Quando usarlo

Usa useId ogni volta che un componente riutilizzabile ha bisogno di un attributo id: campi di form costruiti da un componente usato molte volte, un tooltip collegato con aria-describedby, una finestra di dialogo con aria-labelledby, schede con aria-controls. Quando un id collega solo una label al suo input, puoi anche fare a meno dell'id e annidare l'input dentro la label (<label>Name <input /></label>); usa useId quando gli elementi non possono essere annidati.

Domande frequenti

Cosa fa useId in React?

Restituisce una stringa univoca per quell'istanza del componente che resta la stessa a ogni rendering. La usi per collegare elementi tramite id: htmlFor su una label, aria-describedby su un input, aria-labelledby su una finestra di dialogo.

Perché non usare Math.random() o un contatore per gli id?

Danno valori diversi sul server e nel browser, quindi una pagina renderizzata dal server e la sua versione idratata non concordano e React segnala un hydration mismatch. Math.random() inoltre cambia a ogni rendering. useId ricava l'id dalla posizione del componente nell'albero, che è la stessa in entrambi i posti.

Posso usare useId per le key di una lista?

No. Una key deve arrivare dai tuoi dati così React può abbinare la stessa voce tra un rendering e l'altro. useId viene chiamato una volta per componente, e chiamarlo dentro map viola comunque le regole degli hook. Usa l'id della voce stessa.

Come ottengo più id da una sola chiamata a useId?

Chiama useId una volta e aggiungi dei suffissi: ${id}-name, ${id}-email. La base è univoca, quindi lo sono anche le stringhe con suffisso.

Posso usare l'id di useId in un selettore CSS?

Evitalo. L'id serve a collegare elementi nel DOM, e il suo formato esatto è un dettaglio interno che è cambiato tra una versione di React e l'altra. Dai stile con una classe, e trova gli elementi con un ref invece che con querySelector.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA