react中hooks之useId用法总结以及与useRef用法区别
React useId Hook 使用指南
概述
useId 是 React 18 引入的新 Hook,用于生成唯一的 ID,主要用于可访问性(accessibility)属性。它在服务端和客户端渲染时都能保持一致性。
useId vs useRef
- useId: 生成稳定的唯一标识符,主要用于 HTML 属性关联
- useRef: 存储可变值的容器,主要用于保存引用和状态
基本用法
1. useId 基础示例
function FormField() {
const id = useId();
return (
<div>
<label htmlFor={id}>Name:</label>
<input id={id} type="text" />
</div>
);
}
2. 多个关联 ID
function ComplexForm() {
const id = useId();
return (
<form>
<div>
<label htmlFor={`${id}-name`}>Name:</label>
<input id={`${id}-name`} type="text" />
</div>
<div>
<label htmlFor={`${id}-email`}>Email:</label>
<input id={`${id}-email`} type="email" />
</div>
</form>
);
}
3. ARIA 属性使用
function Accordion() {
const id = useId();
return (
<div>
<button
aria-controls={`${id}-content`}
aria-expanded="false"
>
Toggle
</button>
<div
id={`${id}-content`}
aria-labelledby={`${id}-header`}
>
Content
</div>
</div>
);
}
useId vs useRef 对比
1. 用途区别
// useId: 生成稳定的 ID
function AccessibleInput() {
const id = useId();
return (
<div>
<label htmlFor={id}>Input:</label>
<input id={id} />
</div>
);
}
// useRef: 保存元素引用或可变值
function FocusInput() {
const inputRef = useRef<HTMLInputElement>(null);
const focus = () => {
inputRef.current?.focus();
};
return (
<div>
<input ref={inputRef} />
<button onClick={focus}>Focus</button>
</div>
);
}
2. 生命周期差异
// useId: 组件整个生命周期保持稳定
function StableId() {
const id = useId();
// id 在重渲染时保持不变
return <div id={id}>{id}</div>;
}
// useRef: 可以随时更新值
function Counter() {
const countRef = useRef(0);
const increment = () => {
countRef.current += 1; // 更新值不会触发重渲染
console.log(countRef.current);
};
return <button onClick={increment}>Increment</button>;
}
3. 服务端渲染考虑
// useId: 服务端和客户端保持一致
function ServerComponent() {
const id = useId();
// 在 SSR 和客户端渲染中生成相同的 ID
return <div id={id}>Content</div>;
}
// useRef: 主要用于客户端
function ClientComponent() {
const elementRef = useRef<HTMLDivElement>(null);
// ref 在服务端渲染时为 null
useEffect(() => {
console.log(elementRef.current); // 仅在客户端可用
}, []);
return <div ref={elementRef}>Content</div>;
}
最佳实践
1. 可访问性组件
function AccessibleDialog() {
const id = useId();
return (
<div>
<button
aria-haspopup="dialog"
aria-controls={`${id}-dialog`}
>
Open Dialog
</button>
<div
role="dialog"
id={`${id}-dialog`}
aria-labelledby={`${id}-title`}
aria-describedby={`${id}-desc`}
>
<h2 id={`${id}-title`}>Dialog Title</h2>
<p id={`${id}-desc`}>Dialog description</p>
</div>
</div>
);
}
2. 表单控件关联
function FormGroup({ label, type = "text" }) {
const id = useId();
const errorId = `${id}-error`;
const descriptionId = `${id}-description`;
return (
<div>
<label htmlFor={id}>{label}</label>
<input
id={id}
type={type}
aria-describedby={`${descriptionId} ${errorId}`}
/>
<span id={descriptionId}>Help text</span>
<span id={errorId} role="alert"></span>
</div>
);
}
使用建议
-
useId 适用场景:
- HTML 元素 ID 生成
- ARIA 属性关联
- 表单标签关联
- 需要服务端渲染一致性的场景
-
useRef 适用场景:
- DOM 元素引用
- 存储可变值
- 计时器/订阅的引用
- 需要在不触发重渲染的情况下更新值
-
选择建议:
- 需要稳定的唯一标识符时使用 useId
- 需要存储可变引用时使用 useRef
- 考虑服务端渲染需求
- 考虑可访问性需求
总结
-
useId 特点:
- 生成稳定的唯一标识符
- 服务端渲染兼容
- 适合可访问性属性
- 组件级别的唯一性
-
useRef 特点:
- 存储可变值
- 不触发重渲染
- 适合 DOM 引用
- 组件内部状态管理
-
使用场景区分:
- HTML/ARIA 属性关联用 useId
- DOM 操作和可变值存储用 useRef
- 考虑渲染一致性需求
- 考虑值更新特性