react hook: useRef

时间:2024-03-20 09:15:07

在组件顶层调用 useRef 以声明一个 ref
const ref = useRef(initialValue)
console.log(ref.current)
initialValue:ref 对象的 current 属性的初始值。可以是任意类型的值。这个参数在首次渲染后被忽略。
current 返回一个只有一个属性的对象, 初始值为传递的 initialValue。

可以修改 ref.current 属性。与 state 不同,它是可变的。然而,如果它持有一个用于渲染的对象(例如 state 的一部分),那么就不应该修改这个对象。

改变 ref.current 属性时,React 不会重新渲染组件。

useRef 可以保存 DOM 节点的引用

import { useRef } from 'react';

function MyComponent() {
  const inputRef = useRef(null);
  
// ...
  return <input ref={inputRef} />;

function handleClick() {
    inputRef.current.focus();
  }

useRef 也可以保存任何可变的数据,而不用触发组件的重新渲染

// 可以记录定时器id实现防抖
function handleStartClick() {
  const intervalId = setInterval(() => {
    // ...
  }, 1000);
  intervalRef.current = intervalId;
}

useRef 也可以访问子组件的实例

父组件
import React, { useRef } from 'react';
import ChildComponent from './ChildComponent';
const ParentComponent = () => {
  const childRef = useRef();

  const handleClick = () => {
    childRef.current.focusInput();
  };

  return (
    <div>
      <ChildComponent ref={childRef} />
      <button onClick={handleClick}>Focus Input</button>
    </div>
  );
};

export default ParentComponent;

子组件
import React, { forwardRef, useImperativeHandle, useRef } from 'react';

const ChildComponent = forwardRef((props, ref) => {
  const inputRef = useRef(null);

  useImperativeHandle(ref, () => ({
    focusInput() {
      inputRef.current.focus();
    }
  }));

  return <input ref={inputRef} type="text" />;
});

export default ChildComponent;

useRef 也可以useRef 来保存上一次的值,供下次渲染时进行比较或其他操作

不要在渲染期间写入或者读取 ref.current。

function MyComponent() {
  // ...
  // ???? 不要在渲染期间写入 ref
  myRef.current = 123;
  // ...
  // ???? 不要在渲染期间读取 ref
  return <h1>{myOtherRef.current}</h1>;
}

可以在 事件处理程序或者 Effect 中读取和写入 ref。
function MyComponent() {
  // ...
  useEffect(() => {
    // ✅ 可以在 Effect 中读取和写入 ref
    myRef.current = 123;
  });
  // ...
  function handleClick() {
    // ✅ 可以在事件处理程序中读取和写入 ref
    doSomething(myOtherRef.current);
  }
  // ...
}
如果不得不在渲染期间读取 或者写入,那么应该 使用 state 代替。

避免重复创建 ref 的内容

function Video() {
  const playerRef = useRef(new VideoPlayer());
  // ...
虽然 new VideoPlayer() 的结果只会在首次渲染时使用,但是依然在每次渲染时都在调用这个方法。如果是创建昂贵的对象,这可能是一种浪费。

为了解决这个问题,你可以像这样初始化 ref:


function Video() {
  const playerRef = useRef(null);
  if (playerRef.current === null) {
    playerRef.current = new VideoPlayer();
  }
  // ...