React `useRef` Hook: 全面解析

React useRef Hook: 全面解析

useRef 是 React 中用于创建持久化可变引用的核心 Hook,它解决了函数组件中无法直接访问 DOM 元素和保存可变值的问题。

核心特性

  1. 跨渲染持久化:返回的对象在组件整个生命周期中保持不变
  2. 变更不会触发重渲染 :修改 .current 属性不会导致组件更新
  3. 直接访问 DOM 节点:最常用的场景
  4. 保存任意可变值:类似类组件的实例属性

基础语法

javascript 复制代码
const refContainer = useRef(initialValue);
  • initialValue:初始值(通常为 null
  • refContainer.current:访问/修改引用值

主要使用场景

1. 访问 DOM 元素(最常见用法)

jsx 复制代码
function TextInput() {
  const inputRef = useRef(null);

  const focusInput = () => {
    inputRef.current.focus(); // 直接操作DOM
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={focusInput}>聚焦输入框</button>
    </div>
  );
}

2. 存储可变值(不触发重渲染)

jsx 复制代码
function Timer() {
  const [count, setCount] = useState(0);
  const intervalRef = useRef(); // 存储定时器ID

  useEffect(() => {
    intervalRef.current = setInterval(() => {
      setCount(c => c + 1);
    }, 1000);
    
    return () => clearInterval(intervalRef.current);
  }, []);

  return <div>计时: {count} 秒</div>;
}

3. 保存上一次状态

jsx 复制代码
function Counter() {
  const [count, setCount] = useState(0);
  const prevCountRef = useRef();
  
  useEffect(() => {
    prevCountRef.current = count; // 在渲染后更新
  });
  
  return (
    <div>
      <p>当前值: {count}</p>
      <p>上一次值: {prevCountRef.current ?? '无'}</p>
      <button onClick={() => setCount(c => c + 1)}>增加</button>
    </div>
  );
}

4. 存储复杂计算结果(跳过重复计算)

jsx 复制代码
function ExpensiveComponent() {
  const [value, setValue] = useState(0);
  const computedValueRef = useRef();
  
  if (!computedValueRef.current) {
    // 只计算一次
    computedValueRef.current = heavyComputation(value);
  }
  
  return <div>{computedValueRef.current}</div>;
}

useState 的关键区别

特性 useRef useState
触发重渲染
存储类型 可变值 (current 属性) 不可变状态
访问方式 直接访问 .current 通过状态变量
更新机制 同步更新 异步批量更新
适合场景 DOM引用/缓存值/避免重渲染计算 UI渲染相关状态

高级用法

1. 自定义 Hook 封装

javascript 复制代码
// 获取上一次值的Hook
function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  }, [value]);
  return ref.current;
}

2. 结合 forwardRef 暴露组件内部 DOM

jsx 复制代码
const CustomInput = forwardRef((props, ref) => {
  return <input ref={ref} {...props} />;
});

function Parent() {
  const inputRef = useRef();
  // 可直接操作 CustomInput 内部的 input
  return <CustomInput ref={inputRef} />;
}

3. 测量 DOM 元素

jsx 复制代码
function MeasureExample() {
  const divRef = useRef(null);
  const [size, setSize] = useState({});
  
  useLayoutEffect(() => {
    if (divRef.current) {
      setSize({
        width: divRef.current.offsetWidth,
        height: divRef.current.offsetHeight
      });
    }
  }, []);

  return (
    <div ref={divRef}>
      元素尺寸: {size.width} x {size.height}
    </div>
  );
}

注意事项

  1. 不要在渲染期间修改 refs

    修改 ref.current 应放在事件处理、useEffect 或生命周期方法中

  2. 避免过度使用

    优先考虑 React 数据流(props/state),仅当确实需要时才使用 refs

  3. 初始渲染时 current 为 null

    访问 DOM 元素时需做空值检查:

    js 复制代码
    useEffect(() => {
      if (inputRef.current) {
        inputRef.current.focus();
      }
    }, []);
  4. 服务端渲染(SSR)问题

    SSR 期间 refs 不会附加到 DOM,应在 useEffect 中使用


性能优化

jsx 复制代码
function HeavyComponent() {
  const elementRef = useRef();
  
  // 避免在渲染期间进行昂贵操作
  useEffect(() => {
    if (elementRef.current) {
      performExpensiveDOMOperation(elementRef.current);
    }
  }, []);

  return <div ref={elementRef}>...</div>;
}

总结

useRef 是 React 函数组件的"逃生舱",主要解决两类问题:

  1. 访问 DOM 元素(表单聚焦、媒体控制、动画等)
  2. 存储可变值(计时器ID、缓存数据、前值记录等)

正确使用原则:

  • 需要直接操作 DOM 时使用
  • 需要存储与渲染无关的值时使用
  • 避免在渲染期间修改 .current
  • 结合 forwardRef 暴露组件内部 DOM
  • 优先使用 React 数据流,ref 作为最后手段
相关推荐
前端无涯2 小时前
React中setState后获取更新后值的完整解决方案
前端·react.js
西愚wo2 小时前
前端开发者必备:在浏览器控制台批量提取HTML表单字段名(Label)
前端
小鸡吃米…3 小时前
Python - 类属性
java·前端·python
前端不太难3 小时前
Navigation State 驱动的页面调试方法论
开发语言·前端·react.js
用户47949283569153 小时前
你每天都在用的 JSON.stringify ,V8 给它开了“加速通道”
前端·chrome·后端
JIngJaneIL3 小时前
基于java+ vue办公管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
静待雨落3 小时前
Electron无边框窗口如何拖拽以及最大化和还原窗口
前端·electron
沐泽__4 小时前
iframe内嵌页面双向通信
前端·javascript·chrome
小北方城市网4 小时前
第4 课:Vue 3 路由与状态管理实战 —— 从单页面到多页面应用
前端·javascript·vue.js
ohyeah4 小时前
用 Vue3 + Coze API 打造冰球运动员 AI 生成器:从图片上传到风格化输出
前端·vue.js·coze