概览useId 能在服务端与客户端生成稳定一致的标识,避免 SSR 水合后出现不一致。通过该标识可将 label、描述与错误提示可靠关联到输入控件,满足可访问性要求并改善表单体验。基础关联import { useId, useState } from 'react'
export default function Field() {
const id = useId()
const [value, setValue] = useState('')
const hintId = `${id}-hint`
return (
<div>
<label htmlFor={id}>邮箱</label>
<input
id={id}
type="email"
value={value}
onChange={(e) => setValue(e.target.value)}
aria-describedby={hintId}
required
/>
<p id={hintId}>我们仅用于通知</p>
</div>
)
}
错误信息关联import { useId, useState } from 'react'
export function PasswordField() {
const id = useId()
const [error, setError] = useState<string | null>(null)
const errId = `${id}-error`
return (
<div>
<label htmlFor={id}>密码</label>
<input
id={id}
type="password"
aria-errormessage={errId}
aria-invalid={Boolean(error)}
onBlur={(e) => {
const v = e.target.value
setError(v.length < 8 ? '至少 8 个字符' : null)
}}
/>
{error && <p id={errId} role="alert">{error}</p>}
</div>
)
}
复合控件import { useId } from 'react'
export function RadioGroup() {
const gid = useId()
const labelId = `${gid}-label`
return (
<div role="radiogroup" aria-labelledby={labelId}>
<p id={labelId}>选择一个选项</p>
<label><input type="radio" name={gid} value="a" />A</label>
<label><input type="radio" name={gid} value="b" />B</label>
</div>
)
}
工程要点以 useId 为根派生子标识,避免重复或冲突。SSR 与 CSR 保持一致,避免因随机 id 导致水合差异。与表单状态结合,在错误出现时提供 ARIA 提示并维持焦点语义。验证与指标React:19.0+;Next.js:15.0+在 SSR 场景下不产生水合警告,读屏器能正确关联提示与错误信息

发表评论 取消回复