在 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 是用来传递引用穿过组件边界的(在子组件定义处)。
相关推荐
小小小小宇2 小时前
语法全景对照
前端
weixin_704266052 小时前
Spring Boot 入门了解
前端·firefox
冲浪中台2 小时前
如何实现低代码源码级交付和私有化部署
前端·低代码·私有化部署·源代码管理
炒毛豆2 小时前
Vue 3 公共组件从封装到全局注册的极简指南
前端·javascript·vue.js
踩着两条虫2 小时前
VTJ.PRO 在线应用开发平台前端架构
前端·vue.js·ai编程
踩着两条虫2 小时前
VTJ.PRO 在线应用开发平台部署与运维
前端·vue.js·人工智能
Dxy12393102162 小时前
html鼠标定位线
前端·html·计算机外设
sp422 小时前
通过 RootEncoder 进行安卓直播 RTSP 推流
前端
_院长大人_2 小时前
构建一个 Vue 基于el-input的磨损区间选择器组件 —— WearRangeSelector
前端·javascript·vue.js