React hooks之useRef

465 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第21天,点击查看活动详情

useRef 返回一个可变的ref对象,该对象只有一个current属性,该对象在组件的整个生命周期内保持不变,每次重新渲染后都不会重新初始化值,和useState一样,不同的是修改后不会重新渲染组件re-render

使用

语法类型定义

// 语法类型定义
interface MutableRefObject<T> {
    current: T;
}
function useRef<T>(initialValue: T): MutableRefObject<T>;

useRef函数接收一个任类型的参数作为初始化值initialValue,返回一个对象,对象中只有一个属性current,首次渲染时会将initialValue值赋给current属性,该对象是一个被冻结对象,不能添加其他属性不能删除current属性。

   // 源码部分
   var ref = {
      current: initialValue
    };

    {
      Object.seal(ref);
    }

    hook.memoizedState = ref;

因此我们不管是定义基本数据类型还是其他类型,本质上内部其实都是引用类型,React会将该数据存储上,每次重新渲染时其实使用的都是它的引用,所以所有渲染周期中都用的是最新的值,在所有渲染周期都是可变的,但useState定义的数据在不同渲染周期中是相互独立的,那么使用是也是独立的,如下:

const Demo: React.FC = (props) => {
  const [count, setCount] = useState<number>(0);
  const countRef = useRef(0);
  function onClick() {
    setCount((prevCount: number) => {
      return count + 1;
    });
    countRef.current = countRef.current + 1;
  }
  function onTip() {
    setTimeout(() => {
      alert(`useState:${count};useRef${countRef.current}`);
    }, 5000)
  }
  return (
    <React.Fragment>
      <div onClick={onClick}>点击:{count}</div>
      <div onClick={onTip}>提示</div>
    </React.Fragment>
  )
}

image.png 先点击提示,下来一直连续点击点击,触发每次重新渲染组件,最后提示useState的值为0,而useRef的值为1。因为每次重新渲染会重新定义useState的count,并赴一个新值,而useRef不会重新定义,我们只是给它重新赋新值,引用没有变, onTip方法每次渲染重新定义,每次用的都是当时定义的变量在内存中的位置指向的值,因为useRef定义的它的内存位置一直没变,只是值变了,而useState定义的基本类型每次定义的内存位置都不一样,所以提示的是调用onTip时的值。

使用场景

根据它的特性在所有render渲染时拿到的都是同一个引用,值都是最新的,我们常用于访问DOM节点,对DOM进行操作,监听事件等。

const Demo: React.FC = (props) => {
  const inputRef = useRef<HTMLInputElement>(null);
  function onTip() {
    alert(inputRef.current?.value);
  }
  return (
    <React.Fragment>
      <input ref={inputRef} defaultValue="你好" />
      <div onClick={onTip}>提示</div>
    </React.Fragment>
  )
}