useId là một React Hook để tạo ID duy nhất có thể được truyền cho các thuộc tính hỗ trợ tiếp cận.

const id = useId()

Tham khảo

useId()

Gọi useId ở cấp cao nhất của component để tạo một ID duy nhất:

import { useId } from 'react';

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

Xem thêm các ví dụ bên dưới.

Tham số

useId không nhận bất kỳ tham số nào.

Giá trị trả về

useId trả về một chuỗi ID duy nhất được liên kết với lệnh gọi useId cụ thể này trong component cụ thể này.

Lưu ý

  • useId là một Hook, vì vậy bạn chỉ có thể gọi nó ở cấp cao nhất của component hoặc Hook của riêng bạn. Bạn không thể gọi nó bên trong vòng lặp hoặc điều kiện. Nếu bạn cần điều đó, hãy trích xuất một component mới và di chuyển state vào đó.

  • useId không nên được sử dụng để tạo key trong một danh sách. Key nên được tạo từ dữ liệu của bạn.

  • useId hiện không thể được sử dụng trong Server Components không đồng bộ.


Cách sử dụng

Chú Ý

Không gọi useId để tạo key trong một danh sách. Key nên được tạo từ dữ liệu của bạn.

Tạo ID duy nhất cho các thuộc tính hỗ trợ tiếp cận

Gọi useId ở cấp cao nhất của component để tạo một ID duy nhất:

import { useId } from 'react';

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

Sau đó, bạn có thể truyền ID đã tạo cho các thuộc tính khác nhau:

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

Hãy cùng xem qua một ví dụ để thấy khi nào điều này hữu ích.

Các thuộc tính hỗ trợ tiếp cận HTML như aria-describedby cho phép bạn chỉ định rằng hai thẻ có liên quan đến nhau. Ví dụ: bạn có thể chỉ định rằng một phần tử (như một input) được mô tả bởi một phần tử khác (như một đoạn văn).

Trong HTML thông thường, bạn sẽ viết nó như thế này:

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

Tuy nhiên, việc mã hóa cứng các ID như thế này không phải là một phương pháp tốt trong React. Một component có thể được render nhiều lần trên trang—nhưng ID phải là duy nhất! Thay vì mã hóa cứng một ID, hãy tạo một ID duy nhất với 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>
</>
);
}

Bây giờ, ngay cả khi PasswordField xuất hiện nhiều lần trên màn hình, các ID được tạo sẽ không xung đột.

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

Xem video này để thấy sự khác biệt trong trải nghiệm người dùng với các công nghệ hỗ trợ.

Chú Ý

Với server rendering, useId yêu cầu một cây component giống hệt nhau trên server và client. Nếu cây bạn render trên server và client không khớp chính xác, các ID được tạo sẽ không khớp.

Tìm hiểu sâu

Tại sao useId tốt hơn một bộ đếm tăng dần?

Bạn có thể tự hỏi tại sao useId tốt hơn việc tăng một biến toàn cục như nextId++.

Lợi ích chính của useId là React đảm bảo rằng nó hoạt động với server rendering. Trong quá trình server rendering, các component của bạn tạo ra đầu ra HTML. Sau đó, trên client, hydration đính kèm các trình xử lý sự kiện của bạn vào HTML đã tạo. Để hydration hoạt động, đầu ra của client phải khớp với HTML của server.

Điều này rất khó để đảm bảo với một bộ đếm tăng dần vì thứ tự mà Client Components được hydrate có thể không khớp với thứ tự mà HTML của server được phát ra. Bằng cách gọi useId, bạn đảm bảo rằng hydration sẽ hoạt động và đầu ra sẽ khớp giữa server và client.

Bên trong React, useId được tạo từ “đường dẫn cha” của component gọi. Đây là lý do tại sao, nếu cây client và server giống nhau, thì “đường dẫn cha” sẽ khớp nhau bất kể thứ tự rendering.


Nếu bạn cần cung cấp ID cho nhiều phần tử liên quan, bạn có thể gọi useId để tạo một tiền tố chung cho chúng:

import { useId } from 'react';

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

Điều này cho phép bạn tránh gọi useId cho mọi phần tử cần một ID duy nhất.


Chỉ định một tiền tố chung cho tất cả các ID được tạo

Nếu bạn render nhiều ứng dụng React độc lập trên một trang, hãy truyền identifierPrefix làm một tùy chọn cho các lệnh gọi createRoot hoặc hydrateRoot của bạn. Điều này đảm bảo rằng các ID được tạo bởi hai ứng dụng khác nhau không bao giờ xung đột vì mọi identifier được tạo bằng useId sẽ bắt đầu bằng tiền tố riêng biệt mà bạn đã chỉ định.

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


Sử dụng cùng một tiền tố ID trên client và server

Nếu bạn render nhiều ứng dụng React độc lập trên cùng một trang, và một số ứng dụng này được render phía server, hãy đảm bảo rằng identifierPrefix bạn truyền cho lệnh gọi hydrateRoot ở phía client giống với identifierPrefix bạn truyền cho API server như 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' }
);

Bạn không cần phải truyền identifierPrefix nếu bạn chỉ có một ứng dụng React trên trang.