useId
é um Hook do React para gerar IDs únicos que podem ser passados para atributos de acessibilidade.
const id = useId()
Referência
useId()
Chame useId
no nível superior do seu componente para gerar um ID único:
import { useId } from 'react';
function PasswordField() {
const passwordHintId = useId();
// ...
Parâmetros
useId
não aceita nenhum parâmetro.
Retorna
useId
retorna uma sequência de ID único associada a esta chamada useId
específica neste componente específico.
Ressalvas
-
useId
é um Hook, então você só pode chamá-lo no nível superior do seu componente ou no seus próprios Hooks. Você não pode chamá-lo dentro de loops ou condições. Se precisar, extraia um novo componente e mova o estado para ele. -
useId
não deve ser usado para gerar chaves em uma lista. As chaves devem ser geradas a partir de seus dados.
Uso
Gerando IDs únicos para atributos de acessibilidade
Chame useId
no nível superior do seu componente para gerar um ID único:
import { useId } from 'react';
function PasswordField() {
const passwordHintId = useId();
// ...
Você pode então passar o ID gerado para diferentes atributos:
<>
<input type="password" aria-describedby={passwordHintId} />
<p id={passwordHintId}>
</>
Vamos analisar um exemplo para ver quando isso é útil.
Atributos de acessibilidade do HTML como aria-describedby
permite especificar que duas tags estão relacionadas entre si. Por exemplo, você pode especificar que um elemento (como um input) seja descrito por outro elemento (como um parágrafo).
No HTML normal, você escreveria assim:
<label>
Senha:
<input
type="password"
aria-describedby="password-hint"
/>
</label>
<p id="password-hint">
A senha deve conter pelo menos 18 caracteres
</p>
No entanto, codificar IDs como esse não é uma boa prática no React. Um componente pode ser renderizado mais de uma vez na página, mas os IDs devem ser únicos! Em vez de codificar um ID, gere um ID único com useId
:
import { useId } from 'react';
function PasswordField() {
const passwordHintId = useId();
return (
<>
<label>
Senha:
<input
type="password"
aria-describedby={passwordHintId}
/>
</label>
<p id={passwordHintId}>
A senha deve conter pelo menos 18 caracteres
</p>
</>
);
}
Agora, mesmo que PasswordField
apareça várias vezes na tela, os IDs gerados não entrarão em conflito.
import { useId } from 'react'; function PasswordField() { const passwordHintId = useId(); return ( <> <label> Senha: <input type="password" aria-describedby={passwordHintId} /> </label> <p id={passwordHintId}> A senha deve conter pelo menos 18 caracteres </p> </> ); } export default function App() { return ( <> <h2>Escolha uma senha</h2> <PasswordField /> <h2>Confirme a senha</h2> <PasswordField /> </> ); }
Assista à esse vídeo para ver a diferença na experiência do usuário com tecnologias assistivas.
Deep Dive
Você pode estar se perguntando por que useId
é melhor do que incrementar uma variável global como nextId++
.
O principal benefício do useId
é que o React garante que funcione com a renderização do servidor. Durante a renderização do servidor, seus componentes geram saídas HTML. Posteriormente, no cliente, a hidratação anexa seus manipuladores de eventos ao HTML gerado. Para que a hidratação funcione, a saída do cliente deve corresponder ao HTML do servidor.
Isto é muito difícil de garantir com um contador incremental porque a ordem em que os componentes do cliente são hidratados pode não corresponder à ordem em que o HTML do servidor foi emitido. Ao chamar useId
, você garante que a hidratação funcionará e que a saída corresponderá entre o servidor e o cliente.
Dentro do React, useId
é gerado a partir do “caminho pai” do componente chamado. É por isso que, se o cliente e a árvore do servidor forem iguais, o “caminho pai” corresponderá, independentemente da ordem de renderização.
Gerando IDs para vários elementos relacionados
Se você precisar fornecer IDs para vários elementos relacionados, você pode chamar useId
para gerar um prefixo compartilhado para eles:
import { useId } from 'react'; export default function Form() { const id = useId(); return ( <form> <label htmlFor={id + '-firstName'}>Primeiro nome:</label> <input id={id + '-firstName'} type="text" /> <hr /> <label htmlFor={id + '-lastName'}>Sobrenome:</label> <input id={id + '-lastName'} type="text" /> </form> ); }
Isso permite que você evite chamar useId
para cada elemento que precisa de um ID único.
Especificando um prefixo compartilhado para todos os IDs gerados
Se você renderizar várias aplicações React independentes em uma única página, passe identifierPrefix
como uma opção para suas chamadas createRoot
ou hydrateRoot
. Isso garante que os IDs gerados pelos dois aplicativos diferentes nunca entrem em conflito porque cada identificador gerado com useId
começará com o prefixo distinto que você especificou.
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 />);
Using the same ID prefix on the client and the server
If you render multiple independent React apps on the same page, and some of these apps are server-rendered, make sure that the identifierPrefix
you pass to the hydrateRoot
call on the client side is the same as the identifierPrefix
you pass to the server APIs such as 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' }
);
You do not need to pass identifierPrefix
if you only have one React app on the page.