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();
// ...
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
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ợ.
Tìm hiểu sâu
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.
Tạo ID cho một số phần tử liên quan
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.