useId es un Hook de React para generar IDs únicos que se pueden pasar a los atributos de accesibilidad.

const id = useId()

Referencia

useId()

Llama a useId en el nivel superior de tu componente para generar un ID único:

import { useId } from 'react';

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

Ver más ejemplos abajo.

Parámetros

useId no toma ningún parámetro.

Devuelve

useId devuelve una cadena de ID única asociada con esta llamada useId llamado en un componente particular.

Advertencias

  • useId es un Hook, así que solo puedes llamarlo en el nivel superior de tu componente o en tus propios hooks. No puedes llamarlo dentro de bucles o condiciones. Si necesitas hacerlo, extrae un nuevo componente y mueve allí el estado.

  • useId no debe usarse para generar keys en una lista. Las keys deben generarse a partir de tus datos.


Uso

Atención

No utilices useId para generar keys en una lista. Las keys deben generarse a partir de tus datos.

Generación de ID únicos para atributos de accesibilidad

Llama a useId en el nivel superior de tu componente para generar un ID único:

import { useId } from 'react';

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

A continuación, puedes pasar el ID generado a los diferentes atributos:

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

Veamos un ejemplo para ver cuándo es útil.

Atributos de accesibilidad HTML como aria-describedby te permiten especificar que dos etiquetas están relacionadas entre sí. Por ejemplo, puedes especificar que un determinado elemento (como una entrada de texto) sea descrito por otro elemento (como un párrafo).

En HTML normal, lo escribirías así:

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

Sin embargo, escribir IDs fijos como este no es una buena práctica en React. Un componente puede renderizarse más de una vez en la página, ¡pero los IDs tienen que ser únicos! En lugar de utilizar un ID fijo, puedes generar un ID único con 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>
</>
);
}

Ahora, incluso si PasswordField aparece varias veces en la pantalla, no habrá conflicto entre los IDs generados.

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

Mira este video para ver la diferencia en la experiencia de usuario con tecnologías de asistencia.

Atención

useId requiere un árbol de componentes idéntico en el servidor y el cliente cuando utilizas renderizado en el servidor. Si los árboles que se renderizan en el servidor y el cliente no coinciden exactamente, los IDs generados no coincidirán.

Profundizar

¿Por qué useId es mejor que un contador incremental?

Puede que te preguntes por qué useId es mejor que incrementar una variable global como nextId++.

El principal beneficio de useId es que React se asegura de que funcione con el renderizado en el servidor. Durante el renderizado en el servidos, tus componentes generan salida HTML. Más tarde, en el cliente, la hidratación adjunta tus controladores de eventos al HTML generado. Para que la hidratación funcione, la salida del cliente debe coincidir con el HTML del servidor.

Esto es muy difícil de garantizar con un contador incremental porque el orden en que se hidratan los Componentes del Cliente puede no coincidir con el orden en que se emitió el HTML del servidor. Al llamar a useId, te aseguras de que la hidratación funcionará y la salida coincidirá entre el servidor y el cliente.

Dentro de React, useId se genera a partir de la «ruta del padre» del componente llamado. Esta es la razón por la que, si el cliente y el árbol del servidor son iguales, la «ruta del padre» coincidirá independientemente del orden del renderizado.


Si necesitas proporcionar IDs a varios elementos relacionados, puedes llamar a useId para generar un prefijo compartido para ellos:

import { useId } from 'react';

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

Esto te permite evitar llamar a useId para cada elemento que necesite un ID único.


Especificación de un prefijo compartido para todos los IDs generados

Si renderizas varias aplicaciones de React independientes en una sola página, puedes pasar identifierPrefix como una opción para las llamadas createRoot o hydrateRoot. Esto garantiza que los IDs generados por las dos aplicaciones diferentes nunca entren en conflicto porque cada identificador generado con useId comenzará con el prefijo distinto que hayas especificado.

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


Uso del mismo prefijo de ID en el cliente y el servidor

Si renderizas múltiples aplicaciones de React independientes en la misma página, y algunas de esas aplicaciones son renderizadas en el servidor, asegúrate de que el prefijo identifierPrefix que le pases a la llamada a hydrateRoot en el lado del cliente sea el mismo identifierPrefix que le pases a las APIs del servidor tales como 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' }
);

No necesitas pasar identifierPrefix si solo tienes una aplicación de React en la página.