useId
是一個 React Hook,用於產生可傳遞給無障礙屬性的唯一 ID。
const id = useId()
參考
useId()
在組件的頂層呼叫 useId
來產生唯一的 ID
import { useId } from 'react';
function PasswordField() {
const passwordHintId = useId();
// ...
參數
useId
不接受任何參數。
回傳值
useId
會傳回與此特定元件中特定 useId
呼叫關聯的唯一 ID 字串。
注意事項
-
useId
是一個 Hook,因此您只能在組件的頂層或您自己的 Hooks 中呼叫它。您不能在迴圈或條件式中呼叫它。如果需要,請提取一個新的組件並將狀態移入其中。 -
useId
不應該用於在列表中產生 key。Key 應該從您的資料中產生。
用法
為無障礙屬性產生獨特的 ID
在組件的頂層呼叫 useId
來產生唯一的 ID
import { useId } from 'react';
function PasswordField() {
const passwordHintId = useId();
// ...
然後您可以將 產生的 ID 傳遞給不同的屬性
<>
<input type="password" aria-describedby={passwordHintId} />
<p id={passwordHintId}>
</>
讓我們來看一個例子,了解何時可以使用它。
HTML 無障礙屬性,例如 aria-describedby
,可讓您指定兩個標籤彼此相關。例如,您可以指定一個元素(例如輸入框)由另一個元素(例如段落)描述。
在一般的 HTML 中,您會這樣寫
<label>
Password:
<input
type="password"
aria-describedby="password-hint"
/>
</label>
<p id="password-hint">
The password should contain at least 18 characters
</p>
然而,在 React 中,硬編碼 ID 並不是一個好習慣。一個組件可能會在頁面上渲染多次,但 ID 必須是唯一的!不要硬編碼 ID,而是使用 useId
產生一個獨特的 ID
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>
</>
);
}
現在,即使 PasswordField
在螢幕上出現多次,產生的 ID 也不會衝突。
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 /> </> ); }
觀看此影片 以了解使用輔助技術的使用者體驗差異。
深入探討
您可能會想知道為什麼 useId
比遞增全域變數(例如 nextId++
)更好。
useId
的主要好處是 React 確保它能與 伺服器渲染 配合使用。在伺服器渲染期間,您的組件會產生 HTML 輸出。稍後,在客戶端上,hydration 會將您的事件處理程序附加到產生的 HTML。為了使 hydration 能正常運作,客戶端輸出必須與伺服器 HTML 相符。
使用遞增計數器很難保證這一點,因為客戶端組件 hydration 的順序可能與伺服器 HTML 發出的順序不符。透過呼叫 useId
,您可以確保 hydration 能正常運作,並且伺服器和客戶端的輸出會相符。
在 React 內部,useId
是從呼叫組件的「父路徑」產生的。這就是為什麼如果客戶端和伺服器樹相同,「父路徑」將會匹配,無論渲染順序為何。
為多個相關元素產生 ID
如果您需要為多個相關元素提供 ID,您可以呼叫 useId
為它們產生一個共用的前綴
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> ); }
這讓您可以避免為每個需要獨特 ID 的元素呼叫 useId
。
為所有產生的 ID 指定共用前綴
如果您在單一頁面上渲染多個獨立的 React 應用程式,請將 identifierPrefix
作為選項傳遞給您的 createRoot
或 hydrateRoot
呼叫。這可確保兩個不同應用程式產生的 ID 永遠不會衝突,因為使用 useId
產生的每個識別碼都會以您指定的不同前綴開頭。
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 />);
在客戶端和伺服器端使用相同的 ID 前綴
如果您在同一個頁面上渲染多個獨立的 React 應用程式,並且其中一些應用程式是在伺服器端渲染的,請確保您在客戶端傳遞給 hydrateRoot
呼叫的 identifierPrefix
與您傳遞給 伺服器 API(例如 renderToPipeableStream
)的 identifierPrefix
相同。
// 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' }
);
如果頁面上只有一個 React 應用程式,則不需要傳遞 identifierPrefix
。