在 React 中,useRef、ref 属性以及 forwardRef 是处理“引用”(访问 DOM 节点或组件实例)的核心概念

在 React 中,useRefref 属性以及 forwardRef 是处理"引用"(访问 DOM 节点或组件实例)的核心概念。它们经常一起使用,但职责完全不同。

以下是它们的核心区别、使用方法及组合示例:

1. 核心概念与区别

特性 ref (属性) useRef (Hook) forwardRef (API)
本质 JSX 中的一个特殊属性 一个 React Hook,用于创建 ref 对象。 一个高阶函数,用于将 ref 转发给子组件。
主要作用 标记"我想访问这个元素/组件"。 1. 创建可变的容器 (ref.current)。 2. 存储不触发重渲染的数据。 3. 获取 DOM 节点。 允许父组件 通过 ref 直接访问函数子组件内部的 DOM 节点。
触发重渲染 (修改 current 不会重渲染)
典型场景 挂在到 <input> 或自定义组件上。 在组件内部初始化引用,存储定时器、DOM 节点等。 编写可复用的自定义组件(如 Input, Button),并希望外部能控制其焦点或动画。

2. 详细用法解析

A. useRef:创建引用的容器

useRef 返回一个对象 { current: ... },这个对象在组件的整个生命周期内保持不变。

两大用途:

  1. 访问 DOM 节点 :配合 ref 属性使用。

  2. 存储可变变量 :类似 state不触发重渲染(适合存定时器、上一次的值等)。

    import { useRef, useEffect } from 'react';

    function MyComponent() {
    // 1. 创建 ref 对象,初始值为 null
    const inputRef = useRef(null);
    const countRef = useRef(0); // 2. 用于存储不触发渲染的变量

    useEffect(() => {
    // 组件挂载后,自动聚焦输入框
    // inputRef.current 此时指向真实的 DOM 节点
    if (inputRef.current) {
    inputRef.current.focus();
    }

    复制代码
     // 修改 countRef.current 不会导致组件重新渲染
     countRef.current += 1; 

    }, []);

    return (
    // 将 ref 对象绑定到 DOM 元素

    );
    }

B. forwardRef:打通父子组件的引用通道

默认情况下,函数组件不能接收 ref 。如果你在函数组件上使用 ref,它会丢失(或者在旧版本报错)。 forwardRef 的作用就是让函数组件能够"透传"这个 ref,使其指向组件内部的某个 DOM 元素。

使用场景: 当你封装了一个通用组件(如 FancyInput),希望使用它的父组件能直接控制它(如调用 .focus())。

复制代码
import { forwardRef } from 'react';

// 定义子组件,使用 forwardRef 包裹
// 注意:第二个参数 ref 是由父组件传下来的
const FancyInput = forwardRef((props, ref) => {
  return (
    <div className="fancy-wrapper">
      <label>{props.label}</label>
      {/* 将接收到的 ref 绑定到内部的 input 上 */}
      <input ref={ref} {...props} />
    </div>
  );
});

export default FancyInput;
C. 组合使用:父组件通过 useRef + forwardRef 控制子组件

这是最完整的链路:父组件创建 ref (useRef) -> 传给子组件 (ref 属性) -> 子组件接收并转发 (forwardRef) -> 绑定到 DOM。

复制代码
import { useRef } from 'react';
import FancyInput from './FancyInput'; // 上面定义的组件

function ParentComponent() {
  // 1. 父组件创建 ref
  const inputElementRef = useRef(null);

  const handleFocusClick = () => {
    // 3. 父组件直接操作子组件内部的 DOM
    if (inputElementRef.current) {
      inputElementRef.current.focus();
      inputElementRef.current.value = "被父组件控制了!";
    }
  };

  return (
    <div>
      {/* 2. 将 ref 传给自定义子组件 */}
      <FancyInput 
        ref={inputElementRef} 
        label="请输入:" 
        placeholder="点击按钮聚焦我"
      />
      
      <button onClick={handleFocusClick}>
        聚焦子组件输入框
      </button>
    </div>
  );
}

3. 常见误区与总结

  1. 为什么不能直接在函数组件上用 ref

    • 函数组件没有实例(不像 Class 组件有 this),所以默认没法把 ref 绑定到组件本身。forwardRef 是一种显式的机制,告诉 React:"我知道我在做什么,请把这个 ref 传递给我内部的某个 DOM"。
  2. useRefuseState 的区别?

    • useState:数据改变 -> 触发重渲染
    • useRef:数据 (current) 改变 -> 不触发重渲染
    • 技巧 :如果你需要记录一个值但不希望界面刷新(例如记录上一次的 props 值,或者存储 setInterval 的 ID),请用 useRef
  3. 什么时候必须用 forwardRef

    • 当你开发库组件通用 UI 组件 (如 Ant Design, Material UI 中的输入框、按钮),且需要支持用户通过 ref 来控制焦点、动画或测量尺寸时。
    • 如果是简单的内部逻辑,通常不需要。

一句话总结

  • useRef 是用来制造引用对象的(在组件内部)。
  • ref 是用来挂载引用到元素上的(在 JSX 中)。
  • forwardRef 是用来传递引用穿过组件边界的(在子组件定义处)。
相关推荐
counterxing3 小时前
我整理了一个免费开发资源目录,还做成了 CLI 和 MCP
前端·agent·ai编程
子兮曰9 小时前
Bun v1.3.14 深度解析:Image API、HTTP/3、全局虚拟存储与五十项变革
前端·后端·bun
kyriewen10 小时前
今天,百年巨头一次砍了9200人,而一个离职科学家的实话让全网睡不着觉
前端·openai·ai编程
问心无愧051311 小时前
ctf show web 入门42
android·前端·android studio
kyriewen11 小时前
老板逼我上AI,我偷偷在浏览器里跑LLaMA,省下20万API费
前端·react.js·llm
Beginner x_u11 小时前
前端八股整理(手写 02)|数组转树、数组扁平化、随机打乱一个数组
前端·数组·数组转树·数组扁平化
KaMeidebaby11 小时前
卡梅德生物技术快报|禽类成纤维细胞 FISH 实验:鸟类性别染色体基因定位技术实现与数据验证
前端·数据库·其他·百度·新浪微博
天若有情67312 小时前
前端高阶性能优化:跳出传统懒加载与预加载,基于用户行为做轻量预判加载
前端·性能优化
小小小小宇12 小时前
前端转后端:SQL 是什么
前端
张元清13 小时前
React Observer Hooks:7 种监听 DOM 而不写样板代码的方式
前端·javascript·面试