React Hooks 指北(七):useRef与Ref

669 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情

useRef介绍

useRef主要有以下作用

  • 获取dom元素
  • 解决hooks陷阱
  • 获取变量上一次的值

一:useRef

0. 唯一性

useRef具有唯一性,组件重新加载时,useRef对象始终指向同一块内存,因此useRef是无法使用useEffect进行监听改变的,是毫无意义的。

const divRef = useRef();
useEffect(()=>{
    //无法监听到divRef改变,没有意义的操作
},[divRef])

<div ref={divRef}></div>

1.获取dom

import React, { useEffect, useRef } from 'react';

export default function App() {
  const pRef = useRef()

  //dom已经渲染完成,操作dom
  useEffect(()=>{
    pRef.current.innerHTML = "dzp"
  },[])
  return (
    <div>
      <p ref={pRef}></p>
    </div>
  );
}

在dom加载完成后,我们获取p元素并添加文本内容

image.png

2. 解决hooks陷阱

hooks陷阱指的是变量更新导致函数组件重新加载,如果组件内部存在闭包,则闭包访问的变量始终是初始引用的变量(闭包特点,变量延迟释放)

经典案例

  export default memo(function Son(props) { 
  const [value,setValue] = useState(1)



  function getValue() {
    setTimeout(() => {
      alert(value)
    }, 2000);
  }

  return <div> 
    {value}
    <div onClick={()=>setValue(value+1)}>修改value</div>
    <div onClick={getValue}>获取value</div>
  </div>
})

11.gif

我们发现,开始value的值是1。点击获取value。然后快速点击修改value。最后获取的值并不是最新的value。这个就是hooks闭包陷阱问题。

解决方案:使用useRef

image.png

上面是官网对useRef的定义,也就是说useRef相当于全局变量,它永远保持唯一性,尽管函数组件数据更新导致页面重新加载,但是useRef保持的仍然是一份相同的数据。因此我们可以用useRef解决上述问题

  export default memo(function Son(props) { 
  const [value,setValue] = useState(1)

  const valueRef = useRef(value)

  function getValue() {
    setTimeout(() => {
      alert(valueRef.current)
    }, 2000);
  }

  return <div> 
    {value}
    <div onClick={()=>{setValue(value+1);valueRef.current++}}>修改value</div>
    <div onClick={getValue}>获取value</div>
  </div>
})

12.gif

我们通过在修改value的同时,让useRef保持的变量也更新,最后输出的始终是useRef最新的数据。

3.获取上次变量的值

需求:有一个变量num,当num改变时,输出num之前的值和当前最新的数据

import {useEffect, useRef, useState } from "react"
export default memo(function Son(props) { 
  const [value,setValue] = useState(1)
  const valueRef = useRef(value)
  useEffect(()=>{
    valueRef.current = value
  },[value])

  return <div> 
    <div onClick={()=>{setValue(value+1)}}>修改value</div>
    <div>以前:{valueRef.current}</div>
    <div>当前:{value}</div>
  </div>
})


13.gif

首次valueRef和value都是1.当点击修改value的值成为2时,触发useEffect的num更新回调函数,重点:触发回调函数执行时,num数据和dom已经更新,也就是说界面value已经显示2并且valueRef已经显示1。然后我们才设置了valueRef的值是2,valueRef的值更新并不会触发组件重新加载。

此处不会的原因是没搞清楚 useEffect更新的回调函数执行是在组件监听数据已经更新并且dom已经渲染在页面后执行。

二:ref

给ref传递回调函数可以监听dom的挂载。之前一次在给弹窗组件初始化表单数据时,使用useRef无法监听到表单组件的挂载,导致数据一直无法赋值给表单组件。使用ref可以完美的监听到组件挂载

<input ref={e=>{
    //e就是组件input
    if(e) {
      e.value = '初始的数据'
    }
  }}/> 

总结

  • useRef可以操作页面dom
  • useRef在组件声明周期内永远是唯一的
  • useRef的唯一性可以解决hooks陷阱,可以获取获取变量上次值