React useRef Hook 深入解析

382 阅读3分钟

useRef 是 React 中一个强大的 Hook,可用于访问dom元素、在函数组件的多次渲染之间持久化保存可变值等。它看似简单,但理解其工作原理和潜在陷阱对于编写高效、可维护的 React 代码至关重要。

一、useRef 的基本使用

1. 创建 ref:

const myRef = useRef(initialValue);
  • initialValue:ref 对象的初始值,可以是任意类型(对象、数组、函数等)。
  • myRef:返回一个可变的 ref 对象,其 .current 属性被初始化为 initialValue

2. 访问 ref:

console.log(myRef.current); // 访问 ref 的当前值
myRef.current = newValue; // 更新 ref 的值
  • ref 的值存储在 .current 属性中,可以通过赋值来更新。

3. 使用场景:

  • 访问 DOM 元素: 最常见的用法是获取 DOM 元素的引用,以便直接操作 DOM。
  • 存储可变值: 在组件的生命周期内存储可变值,而不会触发重新渲染。
  • 保存上一次的值: 比较当前值和上一次的值,用于实现某些逻辑。

二、useRef 解决的问题

1. 函数组件没有实例:

与类组件不同,函数组件没有实例,无法像类组件那样通过 this 来存储可变值。useRef 提供了一种在函数组件中存储可变值的机制。

2. 避免不必要的重新渲染:

使用 useState 存储可变值会导致组件重新渲染,而 useRef 不会。这对于存储与渲染无关的数据非常有用,例如定时器 ID、DOM 元素引用等。

三、useRef 使用中的常见问题

1. 滥用 useRef 导致内存泄漏:

如果在组件卸载时没有清理 useRef 存储的值(例如定时器、事件监听器),可能会导致内存泄漏。

解决方案: 在 useEffect 的清理函数中清理 useRef 存储的值。

const timerRef = useRef(null);
useEffect(() => {
  timerRef.current = setInterval(() => {
    // ...
  }, 1000);

  return () => {
      clearInterval(timerRef.current);
      timerRef.current = null;
  };
}, []);

2. 在渲染期间修改 ref 的值:

在渲染期间修改 ref 的值会导致组件行为不可预测,因为 React 依赖于组件的纯函数性质。

解决方案: 只在事件处理函数或 useEffect 等副作用中修改 ref 的值。

3. 将 ref 用于不应该使用的地方:

useRef 不应该用于存储与渲染相关的状态,例如表单输入值。这种情况下应该使用 useState。

四、useRef 的进阶用法

1. 结合 forwardRef 实现 ref 转发:

forwardRef 可以将 ref 从父组件传递到子组件,用于访问子组件的 DOM 元素或实例方法。

const MyInput = forwardRef((props, ref) => {
  return <input ref={ref} {...props} />;
});

function App() {
  const inputRef = useRef(null);

  const focusInput = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <MyInput ref={inputRef} />
      <button onClick={focusInput}>Focus Input</button>
    </div>
  );
}

2. 使用 useImperativeHandle 自定义 ref 的值:

useImperativeHandle 可以自定义 ref 的值,例如只暴露子组件的特定方法。

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

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

  return <input ref={inputRef} {...props} />;
});

五、总结

useRef 是一个强大的工具,但需要谨慎使用。理解其工作原理和潜在陷阱对于编写高效、可维护的 React 代码至关重要。

关键点:

  • useRef 用于在函数组件的多次渲染之间持久化保存可变值。
  • useRef 不会触发组件重新渲染。
  • 避免滥用 useRef 导致内存泄漏。
  • 只在事件处理函数或 useEffect 等副作用中修改 ref 的值。
  • 使用 forwardRef 和 useImperativeHandle 可以实现更高级的 ref 用法。