← 返回题目列表

React useId 解决了什么问题?

中等 第 25 / 27 题 更新于 2026/07/29
ReactuseIdSSR无障碍

简化版

useId 用来生成稳定且 SSR 友好的唯一 ID,常用于表单 label 与 input、ARIA 属性等无障碍关联。它不是列表 key 生成器,也不应用来生成业务 ID。

详细版

组件复用时,手写固定 id 容易重复。

function Field() {
  const id = useId()
  return (
    <>
      <label htmlFor={id}>用户名</label>
      <input id={id} />
    </>
  )
}

useId 生成的 ID 能在服务端渲染和客户端 hydration 时保持匹配,减少随机 ID 导致的结构不一致。它适合 DOM 关联,不适合 key,因为 key 应来自数据身份。

完整版教学

一、问题来自组件复用下的 id 冲突

HTML 的 id 在页面内应该唯一。 如果一个表单组件内部写死 id="name",页面复用两次就会冲突。 label 点击可能聚焦到错误 input,无障碍树也会混乱。

function BadField() {
  return (
    <>
      <label htmlFor="name">姓名</label>
      <input id="name" />
    </>
  )
}

如果页面有 3 个 BadField,就有 3 个相同 id。 浏览器不会替你报错,但行为可能悄悄变坏。

二、useId 生成组件实例级稳定 ID

useId 每个组件实例会得到不同 ID。 组件重新渲染时 ID 保持稳定。 这适合用来关联 label、描述文本、错误提示和 ARIA 属性。

function TextField({ label, error }) {
  const id = useId()
  const errorId = `${id}-error`

  return (
    <div>
      <label htmlFor={id}>{label}</label>
      <input id={id} aria-describedby={error ? errorId : undefined} />
      {error && <p id={errorId}>{error}</p>}
    </div>
  )
}
用途是否适合 useId原因
label htmlFor适合DOM 关联
aria-describedby适合无障碍关联
列表 key不适合key 应来自数据身份
数据库主键不适合不是业务 ID

这里的重点是“DOM 辅助 ID”,不是“万用唯一 ID”。

三、SSR 友好是它的重要价值

如果在渲染时使用 Math.random() 生成 id,服务端和客户端生成的值可能不同。 hydration 时 React 会发现属性不一致。 useId 能根据组件树位置生成可匹配的 ID,适合 SSR。

// 不推荐
const id = Math.random().toString(36)
服务端: id=a1
客户端: id=b9
hydration 属性不一致

对于 Next.js 这类 SSR 框架,稳定 ID 很重要。 否则你可能为了一个表单 id 引入 hydration warning。

四、不要把 useId 当列表 key

列表 key 要描述数据身份。 如果一条 todo 的 id 是 42,key 就应该用 42。 useId 描述的是组件实例中的 DOM ID,不描述业务数据。

todos.map(todo => (
  <TodoItem key={todo.id} todo={todo} />
))

如果用 useId 给列表生成 key,你会把 React 的复用逻辑和数据身份割裂。 排序、插入、删除时可能出现不必要重建或状态错位。 这和 key 的本质相违背。

五、多个相关 ID 可以基于同一个前缀派生

一个组件可能需要 input id、hint id、error id。 不必调用很多次 useId。 可以用一次生成前缀,再拼接后缀。

const baseId = useId()
const inputId = `${baseId}-input`
const hintId = `${baseId}-hint`
const errorId = `${baseId}-error`

如果页面有 20 个表单项,每项派生 3 个 ID,一共 60 个 DOM ID。 用统一前缀能保持可读性和唯一性。

六、它服务无障碍,不只是消除警告

正确的 label 和 aria 关联能让屏幕阅读器理解表单结构。 错误提示、说明文字和控件之间需要明确关系。 useId 提供的是基础设施,真正的目标是可访问的组件。

记忆钩子:useId 给 DOM 关系发号码牌,不给数据身份发身份证。

面试回答时把 label/input 例子写出来最稳。 再强调 SSR 稳定和不能当 key。

七、常见误区与追问

  • 误区:useId 可以用来生成列表 key。 key 应来自数据稳定身份,useId 用于 DOM 关联 ID。
  • 误区:Math.random 生成 id 也一样。 SSR 时随机值可能导致服务端和客户端不匹配。
  • 误区:useId 是业务唯一 ID。 它不应该作为数据库主键、订单号或持久化 ID。
  • 追问:useId 常见场景是什么? 表单 label/input、错误提示、说明文字、ARIA 属性关联。
  • 追问:多个关联元素怎么办? 用一个 useId 作为前缀,拼接 -hint-error 等后缀。
  • 追问:为什么组件库会用它? 组件库无法要求用户手动传唯一 id,useId 能提供稳定默认值。

八、加强记忆

useId 就记两条边界:它适合生成 SSR 稳定的 DOM 关联 ID,尤其是无障碍场景;它不适合列表 key 和业务 ID。讲例子时用 label、input、aria-describedby,一下就落地了。