前言
有时候我们需要通过 ref 获取某个组件的 ref,并使用 ref 提供的功能,组件化是,我们还可能需要自定义组件,并要给外部 ref 供外部使用,因此也会用到 forwardRef + useImperativeHandle 相关功能
这篇文章介绍 hook 的下 ref 的获取,以及自定义组件并暴露 ref 相应功能的内容
ref
平时我们可以通过 ref 获取某个组件的 ref,并使用 ref 提供的功能,很简单的功能,如果我们要设置
const Index = () => {
const ref = React.useRef(null)
return (
<div ref={ ref }>
</div>
)
}
后面我们发现,ref 不只是 dom 类型,它还可以是很多自定义类型,比如经常使用的 antd
我们封装组件时,有时希望双方能够更加友好交互,仅仅是一个 setState 是不合适的,因此有时会对外暴露一个接口,方便外部调用我们内部提供的方法(避免不必要的 setState),这个接口就是我们的 ref
forwardRef + useImperativeHandle
下面提供一个案例,我们使用自定义 hook 自定义一个组件,有时候需要外面主动刷新一下(暂定一个刷新功能),因此需要用到 forwardRef 转发功能,由于函数组件没有实例,需要使用 useImperativeHandle 接收对象(看名字也知道必要的把手,句柄)
//我们自定义的类型,外部可以通过这个类型来更好使用我们的ref
//对于 js 则不用声明类型了,直接使用,对于使用组件来说,自然是 ts 的组件使用体验更友好
export type TableProRef = {
reload: Function,
...
};
//封装一个函数组件,forwardRef<TableProRef, any>(props, ref)
//forwardRef 支持两个参数 props、ref,而参数的类型泛型顺序是颠倒的,ref、props,因此需要注意,不过点进去看看就不会出出错了
const TableProComponent = forwardRef<TableProRef, any>(props, ref) => {
//前面声明好了泛型,下面就根据type类型返回我们的ref句柄,以提供外部调用的功能
useImperativeHandle(ref, () => ({
reload,
...
}));
const reload = () => onSearch();
...
}
//需要注意的是,有提示要加上 displayName,不然会有警告,这里也加上了,调试时,可能更容易看到hook 组件名称
TableProComponent.displayName = 'CustomTableProComponent';
这样外部就可以直接通过 useRef 获取到的 ref 直接调用里面提供的方法了
const ref = useRef<TableProRef>()
ref.current.reload()
<TableProComponent ref={ref} ... >
二次封装转发ref
如果有二次封装,并需要转发内部组件的 ref,那么直接将要抓发的 ref 传递给内部组件就行了,就不多说了哈
const TableProComponent1 = forwardRef<ActionType, any>((props, ref) => {
//ProTable 的 actionRef 的类型是 ActionType 上面标记即可
return <ProTable actionRef={ref} />;
});