useImperativeHandle
和 useRef
是 React Hooks 中用于处理 DOM 引用和暴露子组件方法的工具。以下是它们的规范使用方法和注意事项。
1. useRef
1.1 基本用法
useRef
用于创建一个可变的引用对象,通常用于访问 DOM 元素或存储可变值。
import React, { useRef, useEffect } from 'react';
function MyComponent() {
const inputRef = useRef(null);
useEffect(() => {
// 聚焦输入框
inputRef.current.focus();
}, []);
return <input ref={inputRef} type="text" />;
}
1.2 使用场景
- 访问 DOM 元素:通过
ref
属性绑定到 DOM 元素。 - 存储可变值:用于存储不需要触发重新渲染的值(如定时器 ID)。
1.3 注意事项
useRef
的.current
属性是可变的,修改它不会触发组件重新渲染。- 不要在渲染过程中修改
ref.current
,这会导致不可预测的行为。
2. useImperativeHandle
2.1 基本用法
useImperativeHandle
用于自定义暴露给父组件的实例值或方法。通常与 forwardRef
一起使用。
import React, { useRef, useImperativeHandle, forwardRef } from 'react';
const ChildComponent = forwardRef((props, ref) => {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
getValue: () => {
return inputRef.current.value;
},
}));
return <input ref={inputRef} type="text" />;
});
function ParentComponent() {
const childRef = useRef(null);
const handleClick = () => {
childRef.current.focus(); // 调用子组件的方法
console.log(childRef.current.getValue()); // 获取子组件的值
};
return (
<div>
<ChildComponent ref={childRef} />
<button onClick={handleClick}>Focus Input</button>
</div>
);
}
2.2 使用场景
- 暴露子组件方法:父组件可以通过
ref
调用子组件的方法。 - 封装复杂逻辑:将子组件的内部逻辑封装,只暴露必要的接口。
2.3 注意事项
useImperativeHandle
必须与forwardRef
一起使用。- 避免过度暴露子组件的内部状态或方法,保持组件的封装性。
3. 综合使用示例
以下是一个结合 useRef
和 useImperativeHandle
的完整示例:
import React, { useRef, useImperativeHandle, forwardRef } from 'react';
// 子组件
const CustomInput = forwardRef((props, ref) => {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
clear: () => {
inputRef.current.value = '';
},
getValue: () => {
return inputRef.current.value;
},
}));
return <input ref={inputRef} type="text" placeholder="Enter something" />;
});
// 父组件
function App() {
const inputRef = useRef(null);
const handleFocus = () => {
inputRef.current.focus();
};
const handleClear = () => {
inputRef.current.clear();
};
const handleLogValue = () => {
console.log('Input Value:', inputRef.current.getValue());
};
return (
<div>
<CustomInput ref={inputRef} />
<button onClick={handleFocus}>Focus Input</button>
<button onClick={handleClear}>Clear Input</button>
<button onClick={handleLogValue}>Log Value</button>
</div>
);
}
export default App;
4. 最佳实践
- 避免滥用
useImperativeHandle
:尽量通过 props 和状态管理组件间的通信,只有在必要时才使用useImperativeHandle
。 - 保持
ref
的单一职责:ref
应该只用于访问 DOM 或暴露必要的子组件方法,避免存储复杂状态。 - 类型安全:在使用 TypeScript 时,为
ref
和暴露的方法定义明确的类型。
通过以上规范,可以更清晰、高效地使用 useRef
和 useImperativeHandle
,提升代码的可维护性和可读性。