Ref 的使用
概述
Hook useRef
创建一个引用对象,该对象存在于组件的整个生命周期,不随组件重渲染而重新创建。
引用对象的结构是 { current: T }
,current
属性指向该对象的引用。
ref
对象的引用既可以是一个值,也可以是一个 DOM
节点。
当传入 useRef
的泛型是 HTML
元素类型且 useRef
的初始值是 null
时,返回的是 DOM
节点引用。
否则返回的是值的引用。
// valueRef.current 是一个值
const valueRef = useRef<number>(0)
// domRef.current 是一个 DOM 节点
const domRef = useRef<HTMLDivElement>(null)
DOM
节点引用的类型是 React.RefObject
,current
只读。而值引用的类型是 React.MutableRefObject
,current
可变。
引用一个值
当 ref
用于引用值的时候,ref.current
可以被读写,但 ref.current
的变化并不会触发组件渲染。
因此当使用 useRef
保存值的时候,你应该仅去保存那些和渲染无关的值。
操作 DOM 节点
当 ref
用于引用 DOM
节点时,ref
将会被转发到 DOM
上,通过 ref.current
获取 DOM
。
function Component() {
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
console.log(ref.current)
}, [])
return <div ref={ref}></div>
}
绑定动态列表
将一个 ref
对象绑定到一个固定 DOM
上很简单,但当 DOM
是动态生成的列表,如何将这些 DOM
绑定到 ref
上?
使用 ref 回调
可以解决这个问题,ref 回调
是一个参数为 DOM
节点的函数,当组件挂载或卸载时被调用。
下面是一个示例,我实现了一个 useMultiRef
Hook,用于保存动态列表。
暴露 DOM 节点
通过 forwardRef
可以把组件内部的 DOM
节点暴露给父组件。
interface Props {}
const Component = forwardRef<HTMLDivElement, Props>((props, ref) => {
return <div ref={ref}></div>
})
暴露命令式句柄
通过 useImperativeHandle
可以把组件内部的命令式句柄暴露给父组件。
此时父组件通过 ref
拿到的不是 DOM
节点,而是自定义的句柄对象。
interface Props {}
interface Handle {
a: number
}
export const Component = forwardRef<Handle, Props>((props, ref) => {
useImperativeHandle(
ref,
() => {
return {
a: 1
}
},
[]
)
return <div></div>
})