hooks 系列四:useRefs

1,093 阅读4分钟

快来加入我们吧!

"小和山的菜鸟们",为前端开发者提供技术相关资讯以及系列基础文章。为更好的用户体验,请您移至我们官网小和山的菜鸟们 ( xhs-rookies.com/ ) 进行学习,及时获取最新文章。

"Code tailor" ,如果您对我们文章感兴趣、或是想提一些建议,微信关注 “小和山的菜鸟们” 公众号,与我们取的联系,您也可以在微信上观看我们的文章。每一个建议或是赞同都是对我们极大的鼓励!

前言

这篇文章,我们主要目的是了解一下 useRefs 的使用.

useRefs

定义

useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。

const refContainer = useRef(initialValue)

我们都知道,React 已经提供了一个 API createRef,它的作用同样也是创建一个 ref,那么这个 useRefs Hook 出来的意义是什么呢?它们之间又有什么区别呢?

useRef 的特性

useRef 一个很重要的特性就是:useref 返回的 ref 对象是可变的。正如官网文档中所说,它像一个变量,像可以保存一个可变值的“盒子”。

我们已经知道的 createRef 返回的 ref 对象在每次渲染时都会返回一个新的引用,而 useRef 则返回的是相同的引用 也正如定义中所说,返回的 ref 对象在组件的整个生命周期内保持不变。这也是为什么 useRef 可以在其 .current 属性中保存一个可变值的重要原因。

可能这比较难以理解,那我们用一个例子来理解一下:

function about() {
  const [count, addCount] = useState(0)
  const refForUseRef = useRef()
  const refForCreateRef = createRef()

  if (!refForUseRef.current) {
    // 如果不存在则赋值
    refForUseRef.current = count
  }
  if (!refForCreateRef.current) {
    refForCreateRef.current = count
  }

  return (
    <>
      <div>现在count的值为:{count}</div>
      <div>refForUseRef的值为:{refForUseRef.current}</div>
      <div>refForCreateRef的值为:{refForCreateRef.current}</div>
      <button onClick={() => addCount((val) => val + 1)}>点击+1</button>
    </>
  )
}

看一下效果,就算组件重新渲染,由于 refForUseRef 的值一直存在,所以无法重新赋值,这就是为什么说,useRef 返回的对 ref 的引用是相同的,且在整个生命周期内保持不变。

useRefs-gif1.gif

useRef 在 Hook 中的作用

我们都知道,Hook 的出现,让我们可以在函数组件中就可以做到 Class 组件中的一些特性,我们需要注意一点,Class 组件中有一个概念叫实例变量,那么基于 Hook 的函数组件存在类似实例变量吗?

答案是肯定的,useRef Hook 不仅可以用于 DOM refs,他还有一个重要的作用,就是容纳一个任意值的类似 Class 的实例属性,这也是前面提到的关于它的特性。

我们还是用一个例子,来感受一下使用 useRef 的函数组件的魅力吧。

用例子感受 useRef

不使用 useRef 的函数组件

function about() {
  const [count, addCount] = useState(0)

  function handleAlertClick() {
    setTimeout(() => {
      alert('弹框的count值:' + count)
    }, 2000)
  }

  return (
    <div>
      <div>现在count的值为:{count}</div>
      <button onClick={() => addCount((val) => val + 1)}>点击+1</button>
      <button onClick={() => handleAlertClick()}>展示弹框</button>
    </div>
  )
}

观察这个例子的效果,我们可以发现,弹框里的 count 值是在点击展示弹框按钮时的值,并不是 count 的实时状态,这是为什么呢?

useRefs-gif2.gif

其实,当我们更新状态的时候,React 会重新渲染组件,每一次渲染都会拿到独立的 count 状态, 并重新渲染一个 handleAlertClick 函数. 每一个 handleAlertClick 里面都有它自己的 count 所以每次弹框展示的就是点击时的 count 值。

如何让弹框里的值实时展示 count 值呢?

这个时候就用到了我们一直在讨论的 useRef了,直接看例子:

function about() {
  const [count, addCount] = useState(0)
  const refForUseRef = useRef(count)

  useEffect(() => {
    refForUseRef.current = count
  })

  function handleAlertClick() {
    setTimeout(() => {
      alert('弹框的count值:' + refForUseRef.current)
    }, 2000)
  }

  return (
    <div>
      <div>现在count的值为:{count}</div>
      <div>refForUseRef的值为:{refForUseRef.current}</div>
      <button onClick={() => addCount((val) => val + 1)}>点击+1</button>
      <button onClick={() => handleAlertClick()}>展示弹框</button>
    </div>
  )
}

因为 useRef 每次都会返回同一个引用, 所以在 useEffect 中修改的时候 ,在 alert 中也会同时被修改. 这样子, 点击的时候就可以弹出实时的 count 了.

useRefs-gif3.gif

在这个例子中,count 就类似于 Class 组件中的实例变量,useRef 让我们在函数组件中完成一些 Class 组件的功能。

总结

通过这一篇文章,我们了解到了 React Hook 为我们带了的一个钩子 useRef ,它返回的 ref 对象在组件的整个生命周期保持不变,让我们在函数组件中,也可以像 Class 组件一样保存一些实例属性,为我们开发带来了许多可能性,除了这些新奇的功能,不要忘记 ref 开始获取 DOM 属性的功能,因为在 useRef 同样可以适用。

下节预告

在下节中,我们将为大家介绍 useCallBack ,敬请期待!