Refs 转发是一项将 ref 自动地通过组件传递到其子组件的技巧
为什么需要refs转发
某些组件需要管理其选中状态、焦点、媒体播放、动画执行等,这个时候需要在父组件中处理子组件的副作用
类组件的refs转发
将父组件内定义的ref传递给子组件,即可获取到子组件实例和实例方法属性
函数组件的refs转发
和类组件不同,类组件拥有实例,根据实例可以拿到实例方法,但函数组件的属性方法无法再父组件中获取到。
如果你是一个库的开发者的话,使用该库的人是不知道库的组件类别的,那么当库组件类别是 FunctionComponent 的时候,使用者想用ref获取库组件,怎么办?
React 官方也表述了 ref 的使用条件:
forwardRef
通常在父组件内定义的子组件属性会以一个props对象传入给子组件,forwardRef作用是将传入的ref属性,作为子组件的第二个参数传入
const ChildrenRef = React.forwardRef(Children)
function Children(props, ref) {}
function Parent(){
const ref = useRef();
return (
<ChildrenRef ref={ref} />
)
}
根本原理还是避免将ref直接传给子组件,因为直接传ref的方式在类组件中代表绑定整个子组件,但是在函数组件中没有实例无法绑定整个子组件,所以传ref会存在歧义。
不使用forwardRef
绑定子组件dom的方式是将父组件中定义的ref传入一个非ref名称的子组件属性
function Children(props) {return <div ref={props.onRef}>123</div>}
function Parent(){
const ref = useRef();
return (
<Children onRef={ref} />
)
}
useImperativeHandle
使用useImperativeHandle,父组件可以获取子组件中定义的方法属性
这个函数有三个参数:ref,callback,deps
函数的作用是将回调函数的结果绑定到ref的current属性上,并且根据依赖更新这个绑定过程
const inputRef = useRef();
useImperativeHandle(props.onRef, () => ({
onfocus: () => {
inputRef.current.focus();
}
}), []);
实质还是将方法属性存到了ref中,因为所有对ref的操作都是在同一个对象上。
不使用useImperativeHandle
useImperativeHandle的作用与以下实现等价:
const inputRef = useRef();
useEffect(() => {
props.onRef.current = {
onfocus: () => {
inputRef.current.focus();
}
}
}, [props.onRef])