useId 是一个 React 钩子,用于生成可传递给可访问性属性的唯一 ID。

¥useId is a React Hook for generating unique IDs that can be passed to accessibility attributes.

const id = useId()

参考

¥Reference

useId()

在组件的顶层调用 useId 以生成唯一 ID:

¥Call useId at the top level of your component to generate a unique ID:

import { useId } from 'react';

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

请参阅下面的更多示例。

¥See more examples below.

参数

¥Parameters

useId 没有参数。

¥useId does not take any parameters.

返回

¥Returns

useId 返回与此特定组件中的此特定 useId 调用关联的唯一 ID 字符串。

¥useId returns a unique ID string associated with this particular useId call in this particular component.

注意事项

¥Caveats

  • useId 是一个 Hook,所以你只能在你的组件的顶层或者你自己的钩子中调用它。你不能在循环或条件内调用它。如果需要,提取一个新组件并将状态移入其中。

    ¥useId is a Hook, so you can only call it at the top level of your component or your own Hooks. You can’t call it inside loops or conditions. If you need that, extract a new component and move the state into it.

  • useId 不应该用于生成列表中的键。键应该从你的数据中生成。

    ¥useId should not be used to generate keys in a list. Keys should be generated from your data.


用法

¥Usage

易犯错误

不要调用 useId 来生成列表中的键。键应该从你的数据中生成。

¥Do not call useId to generate keys in a list. Keys should be generated from your data.

为可访问性属性生成唯一 ID

¥Generating unique IDs for accessibility attributes

在组件的顶层调用 useId 以生成唯一 ID:

¥Call useId at the top level of your component to generate a unique ID:

import { useId } from 'react';

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

然后,你可以将 generated ID 传递给不同的属性:

¥You can then pass the generated ID to different attributes:

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

让我们通过一个例子来看看这在什么时候有用。

¥Let’s walk through an example to see when this is useful.

HTML 可访问性属性aria-describedby 一样让你指定两个标签相互关联。例如,你可以指定一个元素(如输入)由另一个元素(如段落)描述。

¥HTML accessibility attributes like aria-describedby let you specify that two tags are related to each other. For example, you can specify that an element (like an input) is described by another element (like a paragraph).

在常规 HTML 中,你可以这样写:

¥In regular HTML, you would write it like this:

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

然而,像这样硬编码 ID 在 React 中并不是一个好习惯。一个组件可能会在页面上渲染多次 - 但 ID 必须是唯一的!不要对 ID 进行硬编码,而是使用 useId 生成一个唯一的 ID:

¥However, hardcoding IDs like this is not a good practice in React. A component may be rendered more than once on the page—but IDs have to be unique! Instead of hardcoding an ID, generate a unique ID with 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>
</>
);
}

现在,即使 PasswordField 在屏幕上出现多次,生成的 ID 也不会冲突。

¥Now, even if PasswordField appears multiple times on the screen, the generated IDs won’t clash.

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

看这个视频 以查看辅助技术在用户体验方面的差异。

¥Watch this video to see the difference in the user experience with assistive technologies.

易犯错误

对于 服务器渲染useId 需要服务器和客户端上相同的组件树。如果你在服务器和客户端上渲染的树不完全匹配,则生成的 ID 将不匹配。

¥With server rendering, useId requires an identical component tree on the server and the client. If the trees you render on the server and the client don’t match exactly, the generated IDs won’t match.

深入研究

为什么 useId 比递增计数器更好?

¥Why is useId better than an incrementing counter?

你可能想知道为什么 useId 比增加像 nextId++ 这样的全局变量更好。

¥You might be wondering why useId is better than incrementing a global variable like nextId++.

useId 的主要好处是 React 确保它可以与 服务器渲染。 在服务器渲染期间,你的组件生成 HTML 输出。稍后,在客户端上,hydration 将你的事件处理程序附加到生成的 HTML。要进行水合作用,客户端输出必须与服务器 HTML 相匹配。

¥The primary benefit of useId is that React ensures that it works with server rendering. During server rendering, your components generate HTML output. Later, on the client, hydration attaches your event handlers to the generated HTML. For hydration to work, the client output must match the server HTML.

使用递增计数器很难保证这一点,因为客户端组件的顺序可能与发出服务器 HTML 的顺序不匹配。通过调用 useId,你可以确保水合作用,并且输出将在服务器和客户端之间匹配。

¥This is very difficult to guarantee with an incrementing counter because the order in which the Client Components are hydrated may not match the order in which the server HTML was emitted. By calling useId, you ensure that hydration will work, and the output will match between the server and the client.

在 React 内部,useId 是从调用组件的 “父路径” 生成的。这就是为什么如果客户端和服务器树相同,则无论渲染顺序如何,“父路径” 都会匹配。

¥Inside React, useId is generated from the “parent path” of the calling component. This is why, if the client and the server tree are the same, the “parent path” will match up regardless of rendering order.


¥Generating IDs for several related elements

如果需要为多个相关元素赋予 ID,可以调用 useId 为它们生成一个共享前缀:

¥If you need to give IDs to multiple related elements, you can call useId to generate a shared prefix for them:

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

¥This lets you avoid calling useId for every single element that needs a unique ID.


为所有生成的 ID 指定共享前缀

¥Specifying a shared prefix for all generated IDs

如果你在单个页面上渲染多个独立的 React 应用,请将 identifierPrefix 作为选项传递给你的 createRoothydrateRoot 调用。这可确保两个不同应用生成的 ID 不会发生冲突,因为使用 useId 生成的每个标识符都将以你指定的不同前缀开头。

¥If you render multiple independent React applications on a single page, pass identifierPrefix as an option to your createRoot or hydrateRoot calls. This ensures that the IDs generated by the two different apps never clash because every identifier generated with useId will start with the distinct prefix you’ve specified.

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 前缀

¥Using the same ID prefix on the client and the server

如果你 在同一页面上渲染多个独立的 React 应用,并且其中一些应用是服务器渲染的,请确保你传递给客户端的 hydrateRoot 调用的 identifierPrefix 与传递给 服务器 API(例如 renderToPipeableStream)的 identifierPrefix 相同

¥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' }
);

如果页面上只有一个 React 应用,则无需传递 identifierPrefix

¥You do not need to pass identifierPrefix if you only have one React app on the page.


React 中文网 - 粤ICP备13048890号