当前位置: 首页 > news >正文

在 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 元素 <input ref={inputRef} type="text" placeholder="我会自动聚焦" /> ); }
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是用来传递引用穿过组件边界的(在子组件定义处)。
http://www.jsqmd.com/news/540500/

相关文章:

  • STM32上如何用串口BREAK中断优雅处理DMX与RDM协议(附完整代码)
  • NetGen:高质量网格生成的科学计算解决方案
  • 创龙T113 SDK编译实战:从环境配置到疑难排错
  • 双端适配陪玩系统 JAVA 国际版源码 + H5 + 打手俱乐部集成方案
  • 从项目实战出发:用pip和venv搞定Python多版本依赖隔离与离线部署(附requirements.txt最佳实践)
  • 如何用PortProxyGUI简化Windows端口转发配置
  • 光学设计避坑指南:CODEV10.2中那些容易忽略的细节(附练习题解析)
  • Go-Zero + DTM实战:电商订单与库存的分布式事务处理(附完整代码)
  • 从 0 到 1 搭建企业级 UI 自动化测试框架(Python + Selenium + Pytest + Allure)
  • AHT20传感器数据漂移?STM32硬件I2C与软件模拟的稳定性对比测试
  • 量子阱、量子线和量子点有什么区别?从游泳池到楼梯的通俗解释
  • Python实现简易可信度推理引擎:用20行代码复现经典CF模型
  • Cortex-M架构运行Linux的技术挑战与替代方案
  • 用PyCharm玩转gprMax 3.0:从A扫、B扫到波形堆叠的完整仿真项目实战
  • ThinkPHP+Uniapp实战:如何从零搭建一个企业级设备巡检小程序(附源码)
  • Beyond Compare 5 高效激活方案:开源工具生成授权密钥完整指南
  • Arduino EtherCAT从站开发:基于EsmacatShield的PDO映射与状态机实践
  • 【Qt+FFmpeg】动态时间水印在视频监控回放中的应用
  • 5个核心功能解决Windows音频捕获难题:win-capture-audio的低延迟技术改进
  • 从GCC到Glibc:拆解交叉编译工具链的‘黑盒子‘(以树莓派开发为例)
  • 数据结构从0到入门(1):数据结构概述
  • 如何快速掌握Unity JSON处理:新手必看的5个核心技巧
  • 模型timm/ViT-B-16-SigLIP简要介绍及其应用场景
  • 闲鱼自动化运营工具:如何通过Appium技术实现二手交易效率提升
  • PPTist:革新浏览器端演示文稿创作的无缝解决方案
  • 单电阻采样翻车实录:从SVPWM扇区判断到ADC采样点的那些‘坑’
  • 手把手教你用KAN网络解决偏微分方程:从理论到代码实现
  • 4个步骤让普通用户实现黑苹果EFI自动生成:OpCore Simplify智能工具全解析
  • YOLOv11环境搭建保姆级教程:从安装到快速推理(附常见问题解决)
  • 别再死记硬背了!用GanttPRO或draw.io画图,直观理解FCFS、SJF、优先级调度差异