476 字
2 分钟
useId

为什么需要它
无障碍属性靠 id 把两个元素关联起来:
<label> 密码: <input type="password" aria-describedby="password-hint" /></label><p id="password-hint">密码至少 18 位</p>这样写死在 React 里会出事:同一个组件在页面上渲染两次,id 就重复了,aria-describedby 指向哪个也说不清。
用 useId 生成
import { useId } from 'react'
function PasswordField() { const passwordHintId = useId()
return ( <> <label> 密码: <input type="password" aria-describedby={passwordHintId} /> </label> <p id={passwordHintId}>密码至少 18 位</p> </> )}组件渲染几次,就得到几组互不重复的 id。它不接受参数,返回一个字符串。
一组相关元素共用一个前缀
不必给每个元素都调一次,生成一个前缀再拼接:
const id = useId()
<label htmlFor={id + '-firstName'}>名字:</label><input id={id + '-firstName'} /><label htmlFor={id + '-lastName'}>姓氏:</label><input id={id + '-lastName'} />服务端渲染要求两棵树一致
useId 的值由组件在树里的位置算出来,所以服务端与客户端渲染出的组件树必须完全一致,否则两端算出的 id 对不上,水合会报不匹配。
页面里有多个独立的 React 应用时,给 createRoot 或 hydrateRoot 传 identifierPrefix,避免两个应用生成的 id 撞车。这些应用里如果有服务端渲染的,服务端与客户端要传同一个前缀。
19.2 改了默认前缀
默认前缀从 :r: 改成了 _r_(React 19.2),目的是让 id 的形态符合规范。这条的实用含义是:别把 id 的具体字符串写进测试断言或 CSS 选择器,它不是稳定接口。
三件不要做的事
别用 useId 生成列表的 key。key 要从你自己的数据里来:
// 不行{list.map(item => <li key={useId()}>{item.name}</li>)}
// 可以{list.map(item => <li key={item.id}>{item.name}</li>)}别拿它当 use() 的缓存键。它在组件挂载期间稳定,但渲染过程中可能变化。
它不能用在异步的 Server Component 里。
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 190 天
部分内容可能已过时