useRef、useForwardRef 和 useImperativeHandle
useRef
、useForwardRef
和 useImperativeHandle
是 React 中用于处理引用(refs)的三个不同 Hook,它们的用途和应用场景有明显区别:
1. useRef
- 用途:创建一个可变的 ref 对象,用于存储不需要触发渲染的变量(如 DOM 节点、定时器 ID、任意值)。
- 特点:
- 不触发重新渲染:修改
.current
属性不会触发组件更新。 - 跨渲染周期保留值:每次渲染时返回同一个 ref 对象。
- 不触发重新渲染:修改
- 示例:
jsx
function TextInput() {const inputRef = useRef(null);const focusInput = () => {inputRef.current.focus(); // 访问 DOM 节点};return (<div><input ref={inputRef} type="text" /><button onClick={focusInput}>聚焦输入框</button></div>); }
2. forwardRef
- 用途:将 ref 从父组件传递到子组件的内部元素(通常是 DOM 节点),突破组件封装。
- 特点:
- 函数组件默认不接收 ref,需用
forwardRef
包裹才能接收。 - 常用于自定义组件(如封装 Input、Button 等)。
- 函数组件默认不接收 ref,需用
- 示例:
jsx
// 子组件:使用 forwardRef 接收 ref const CustomInput = forwardRef((props, ref) => {return <input ref={ref} {...props} />; });// 父组件:传递 ref function Parent() {const inputRef = useRef(null);const focusInput = () => {inputRef.current.focus(); // 直接访问 CustomInput 内部的 input};return (<div><CustomInput ref={inputRef} /><button onClick={focusInput}>聚焦</button></div>); }
3. useImperativeHandle
- 用途:与
forwardRef
配合使用,自定义暴露给父组件的 ref 内容(例如,只暴露特定方法而非整个 DOM 节点)。 - 特点:
- 控制 ref 的暴露内容:可以隐藏内部实现细节,只暴露需要的方法或属性。
- 提高封装性:避免直接暴露 DOM 节点。
- 示例:
jsx
// 子组件:自定义暴露的 ref 内容 const CustomInput = forwardRef((props, ref) => {const inputRef = useRef(null);useImperativeHandle(ref, () => ({focus: () => {inputRef.current.focus();},clear: () => {inputRef.current.value = '';}}));return <input ref={inputRef} {...props} />; });// 父组件:只能访问子组件暴露的方法 function Parent() {const inputRef = useRef(null);const handleFocus = () => {inputRef.current.focus(); // 可用// inputRef.current.value = 'xxx'; // 不可用,未暴露};return (<div><CustomInput ref={inputRef} /><button onClick={handleFocus}>聚焦</button></div>); }
总结对比
Hook | 主要用途 | 是否与子组件通信 | 暴露内容 |
---|---|---|---|
useRef | 存储 DOM 节点或可变值,不触发渲染 | 否 | 整个 ref 对象或 DOM 节点 |
forwardRef | 将父组件的 ref 传递到子组件内部的 DOM 节点 | 是 | 完整的 DOM 节点 |
useImperativeHandle | 与 forwardRef 配合,自定义暴露给父组件的内容(如特定方法) | 是 | 自定义的方法或属性 |
使用场景建议
- useRef:内部管理 DOM 或状态(如定时器、表单值)。
- forwardRef:封装可复用组件,需要父组件直接操作内部 DOM。
- useImperativeHandle:限制父组件对 ref 的访问,暴露有限的 API(如只允许调用
focus()
)。