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>
})