概览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 场景下不产生水合警告,读屏器能正确关联提示与错误信息

发表评论 取消回复