在 React 中,forwardRef 和 useImperativeHandle 是两个用于处理组件间引用传递的高级 API,常用于函数组件中暴露 DOM 操作或自定义方法给父组件。以下是详细说明和示例:
一、核心概念
-
forwardRef- 作用:将父组件传递的
ref转发到子组件内部,允许父组件直接访问子组件的 DOM 元素或类组件实例。 - 适用场景:需要父组件直接操作子组件 DOM(如聚焦输入框)或调用子组件方法时。
- 作用:将父组件传递的
-
useImperativeHandle- 作用:自定义暴露给父组件的实例值(如方法或属性),避免直接暴露整个组件实例。
- 适用场景:与
forwardRef配合使用,在函数组件中暴露特定方法给父组件。
二、组合使用场景
当需要让父组件:
- 直接操作子组件的 DOM 元素(如
focus()) - 调用子组件中定义的自定义方法(如
validate())
时,需结合使用这两个 API。
三、完整示例
1. 子组件(CustomInput.js)
import { forwardRef, useRef, useImperativeHandle } from 'react';
const CustomInput = forwardRef((props, ref) => {
const inputRef = useRef(null);
// 自定义暴露给父组件的方法
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
getValue: () => {
return inputRef.current.value;
},
clear: () => {
inputRef.current.value = '';
}
}));
return <input ref={inputRef} {...props} />;
});
export default CustomInput;
2. 父组件(App.js)
import { useRef } from 'react';
import CustomInput from './CustomInput';
function App() {
const inputRef = useRef(null);
const handleFocus = () => {
inputRef.current.focus(); // 调用子组件暴露的 focus 方法
};
const handleGetValue = () => {
const value = inputRef.current.getValue(); // 调用子组件暴露的 getValue 方法
alert(`当前值:${value}`);
};
const handleClear = () => {
inputRef.current.clear(); // 调用子组件暴露的 clear 方法
};
return (
<div>
<CustomInput ref={inputRef} placeholder="输入内容..." />
<button onClick={handleFocus}>聚焦输入框</button>
<button onClick={handleGetValue}>获取值</button>
<button onClick={handleClear}>清空内容</button>
</div>
);
}
export default App;
四、关键点解析
-
forwardRef的作用- 将父组件的
ref转发到子组件的input元素 - 允许父组件通过
ref.current访问子组件内部元素
- 将父组件的
-
useImperativeHandle的作用- 替代直接暴露
inputRef.current,只暴露父组件需要的方法 - 避免父组件意外修改子组件内部状态
- 返回的对象定义了父组件可调用的方法
- 替代直接暴露
-
优势
- 解耦:父组件不直接依赖子组件实现细节
- 安全:通过接口暴露方法,而非直接操作 DOM
- 灵活:可组合多个方法(如示例中的
focus/getValue/clear)
五、典型使用场景
- 表单组件需要暴露验证方法
- 复杂 UI 组件(如自定义下拉菜单)需要暴露打开/关闭方法
- 第三方库组件需要封装特定操作
- 需要父组件直接控制子组件动画/交互行为时
六、注意事项
- 避免过度使用:优先通过 props 驱动状态,ref 操作应作为最后手段
- 类型安全:使用 TypeScript 时需定义暴露方法的类型
- 性能:直接操作 DOM 可能导致非受控组件行为,需谨慎处理状态同步
通过合理使用这两个 API,可以在保持组件封装性的同时,实现必要的跨组件操作能力。