在tsx下使用
默认情况下,我们不能在函数组件上使用 ref
属性,因为它们没有实例。
如果要在函数组件中使用 ref
,可以使用 forwardRef
包裹组件函数使用(可与 useImperativeHandle
结合使用)。
被 forwardRef 包裹的组件函数除 props
,还要多传入第二个参数:ref
,即从外部传入的 ref。
useImperativeHandle
接收三个参数,第一个是 forwardRef
传入的 ref
;第二个参数是 handle
函数,返回要向外暴露的值,通常是对象;第三个参数是依赖数组
,根据依赖变化执行更新,非必需参数。
import { useRef, forwardRef, Ref, useImperativeHandle, ElementRef, useEffect } from "react";
// 组件,有两种定义类型的方式
// 组件1,一种是使用 forwardRef 的泛型
// forwardRef 泛型第一个参数是 Ref 类型,
// 第二个参数是 props 类型,不传时默认类型为{},
// 注意,forwardRef 泛型与内部包裹函数的参数顺序恰恰相反,易造成歧义
const Component1 = forwardRef<HTMLInputElement, {}>((props, ref) => <input ref={ref} />);
// 组件2,另一种是是在函数参数上直接定义类型, **该方式验证有效**
// 注意,在函数参数上定义类型时,ref 参数类型需要使用Ref泛型包裹,而 forwardRef 泛型则不需要
const type useImperativeHandle返回的类型 = React.Ref<{
sayHello: () => void
}>
const Component2 = forwardRef(
// ref 类型使用了 Ref 泛型包裹
(props: {}, ref: useImperativeHandle返回的类型) => {
// ref 为第一个参数,返回带有 sayHello 方法对象的函数为第二个参数
useImperativeHandle(ref, () => {
return {
sayHello: () => console.log("hello"),
};
});
return <div>Say hello in console.log</div>;
}
);
// App
export default function App() {
// 在父组件中使用时需要使用 ElementRef 泛型,并使用 typeof 获取组件的 ref 类型
const textInputRef = useRef<ElementRef<typeof Component1>>(null);
const helloRef: useImperativeHandle返回的类型 = useRef(null); // **该方式验证有效**
useEffect(() => {
// 聚焦textInputRef
if (textInputRef.current) textInputRef.current.focus();
// 调用helloRef的sayHello方法
if (helloRef.current) helloRef.current.sayHello();
}, []);
return (
<>
<Component1 ref={textInputRef} />
<Component2 ref={helloRef} />
</>
);
}
可以看到,有两种方式定义 forwardRef
组件的类型。
一种是在 forwardRef
泛型中定义 ref
类型与 props
类型,需要注意的是泛型中是 ref
类型在前,props
类型在后,而函数组件中是 props
在前,ref
在后。
另一种则是直接在函数组件的参数中定义类型,需要注意的是这种方式定义 ref
类型需要使用 Ref
泛型。
另外在父组件中,使用 useRef
时,则需要通过 ElementRef
泛型与 typeof
结合获取 ref 类型。
No Leanote account? Sign up now.