应用场景
在做可复用组件时,我们常常需要给函数组件传递 ref 属性,以访问或操作组件内部的 DOM 或向外暴露方法等。
import { useRef } from "react";
function Component() {
return <input />;
}
function App() {
const textInputRef = useRef();
return <Component ref={textInputRef} />;
}
在 jsx 下使用
默认情况下,我们不能在函数组件上使用 ref 属性,因为它们没有实例。
如果要在函数组件中使用 ref,可以使用 forwardRef 包裹组件函数使用(可与 useImperativeHandle 结合使用)。
被 forwardRef 包裹的组件函数除 props,还要多传入第二个参数:ref,即从外部传入的 ref。
useImperativeHandle 接收三个参数,第一个是 forwardRef 传入的 ref;第二个参数是 handle 函数,返回要向外暴露的值,通常是对象;第三个参数是依赖数组,根据依赖变化执行更新,非必需参数。
import { useRef, forwardRef, useImperativeHandle, useEffect } from "react";
const Component1 = forwardRef((props, ref) => <input ref={ref} />);
const Component2 = forwardRef((props, ref) => {
useImperativeHandle(ref, () => {
return {
sayHello: () => console.log("hello"),
};
});
return <div>Say hello in console.log</div>;
});
function App() {
const textInputRef = useRef();
const helloRef = useRef();
useEffect(() => {
textInputRef.current.focus();
helloRef.current.sayHello();
}, []);
return (
<>
<Component1 ref={textInputRef} />
<Component2 ref={helloRef} />
</>
);
}
在 tsx 下使用
以上是在 React 中使用 forwardRef 的情况,那么结合了 Typescript,又会发生很多的类型问题,如何在 TS 中使用 forwardRef 呢?
import { useRef, forwardRef, Ref, useImperativeHandle, ElementRef, useEffect } from "react";
const Component1 = forwardRef<HTMLInputElement, {}>((props, ref) => <input ref={ref} />);
const Component2 = forwardRef(
(props: {}, ref: Ref<{ sayHello: () => void }>) => {
useImperativeHandle(ref, () => {
return {
sayHello: () => console.log("hello"),
};
});
return <div>Say hello in console.log</div>;
}
);
export default function App() {
const textInputRef = useRef<ElementRef<typeof Component1>>(null);
const helloRef = useRef<ElementRef<typeof Component2>>(null);
useEffect(() => {
if (textInputRef.current) textInputRef.current.focus();
if (helloRef.current) helloRef.current.sayHello();
}, []);
return (
<>
<Component1 ref={textInputRef} />
<Component2 ref={helloRef} />
</>
);
}
可以看到,有两种方式定义 forwardRef 组件的类型。
另外在父组件中,使用 useRef 时,则需要通过 ElementRef 泛型与 typeof 结合获取 ref 类型。
forwardRef 泛型中,若组件没有用到 props,则 props 类型可不传,默认为{}。示例如下:
const Component = forwardRef<HTMLInputElement>((props, ref) => <input ref={ref}>);
参考资料
declare type with React.useImperativeHandle()
@types/react - github
Forwarding React Refs with TypeScript