🐾
Ref 的使用

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.RefObjectcurrent 只读。而值引用的类型是 React.MutableRefObjectcurrent 可变。

引用一个值

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,用于保存动态列表。

export default function App(): JSX.Element {
  return <h1>Hello world</h1>
}

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

MIT 2024 © Binghuis