Vue 人看 React useRef:它不只是替代 ref
如果你是从 Vue 转到 React 的开发者,初见
useRef可能会想:这不就是 React 版的ref吗?但真相是 —— 它能做的,比你想象得多得多。
👀 Vue 人初见 useRef
在 Vue 中,ref 是我们访问 DOM 或响应式数据的利器。但在 React 中,useRef 并不止是一个获取 DOM 的工具,它更像是一个“不会引起重新渲染的变量容器”。
如果你在想:
- “为啥我改了
ref.current,界面却没更新?” - “这玩意儿跟 Vue 的
ref好像不太一样?” - “它除了操作 DOM 还能干嘛?”
这篇文章,就帮你用 Vue 人的视角,彻底搞懂 useRef 的多种用法与常见陷阱。
🔍 什么是 useRef
引入 React 文档的话:
“
useRef是一个 React Hook,它能帮助引用一个不需要渲染的值”
useRef 创建的是一个普通的 Javascript 对象,里面仅有一个 current 属性,用于读取和修改。
import { useRef } from "react";function Example() {const countRef = useRef(0);countRef.current += 1;
}
useRef 是一个 可变的盒子,你可以把任何值塞进去,它不会重新触发组件 render,但你可以随时取用。
🧪 举个栗子:
export default function Example() {const countRef = useRef(0);console.log("render");return (<button onClick={() => (countRef.current += 1)}>点击count:{countRef.current}</button>);
}
我们创建一个按钮去给 countRef 进行自增,我们看看组件有没有重新 render。

可以看到,虽然 countRef 数据自增了,但是却不会 触发新的渲染。
“当你希望组件“记住”某些信息,但又不想让这些信息 触发新的渲染 时,你可以使用 useRef”
如果你比较熟悉 useState,我们可以举个更简单的例子:
import { useState } from "react";export default function Example() {const [countRef, never] = useState({ current: 0 });return (<button onClick={() => (countRef.current += 1)}>点击count:{countRef.current}</button>);
}
原则上 useRef 可以在 useState 的基础上实现:
不使用
setup函数去改变值,不去触发新的渲染。
了解了基本概念,我们再来看看它与 Vue 的 ref 有哪些关键不同。
⚔️ useRef 和 Vue ref 的区别
| 对比点 | useRef | Vue ref |
|---|---|---|
| 响应性 | 不具备响应性,不触发 render | 具备响应性,数据变化会更新视图 |
| 使用场景 | DOM引用、缓存变量、定时器、历史值等 | DOM引用、响应式数据 |
| 数据结构 | { current: value } | value 是响应式对象 |
| 是否引发视图更新 | 否 | 是 |
Vue 的 ref 是 响应式容器,值变化会自动更新视图;
而 React 的 useRef 更像一个可读写但不具响应性的变量盒子。
🧩 useRef 常见使用场景
前面介绍完 useRef 的基本概念和使用方法,我们接下来看看平时开发中比较常见的使用场景:
1. 定时器引用
我们来实现一个 简易计时器
import { useState, useRef } from "react";export default function Example() {const [time, setTime] = useState(0);const timerRef = useRef(null);const handleStart = () => {if (timerRef.current) return;const startTime = Date.now();timerRef.current = setInterval(() => {setTime(Date.now() - startTime);}, 10);};const handleStop = () => {if (!timerRef.current) return;clearInterval(timerRef.current);timerRef.current = null;console.log("销毁定时器:", timerRef.current);};return (<><h1>计时器: {time}</h1><button onClick={handleStart}>开始</button><button onClick={handleStop}>停止</button></>);
}
- 按下开始键,计时器 开始进行计时,这时候把定时器存到
timerRef中 - 按下停止键,销毁当前定时器,防止出现 闭包导致的内存泄漏。

2. 操作 DOM
我们假定一个场景,用户进入页面时,我们需要用户光标默认 聚焦到输入框。
import { useEffect, useRef } from "react";export default function Example() {const inputRef = useRef(null);useEffect(() => {inputRef.current?.focus();}, []);return <input ref={inputRef} />;
}
我们需要:
- 使用
useRef创建inputRef,默认值为null。 - 使用
ref={inputRef}去存储当前DOM元素。 - 通过
useEffect在进入页面时进行inputRef.current?.focus()
这里类似 Vue 的 ref="xxx" + this.$refs.xxx.focus()。
注意: 不要在渲染过程中读取或写入
ref.current,会使ref变得不可预测。

使用 ref 去存储正常的标签都能正常获取其 DOM 元素,但是当你尝试将 ref 放在 自定义组件 上,会发生什么呢?
3. 绑定自定义组件的 ref
我们先来实践一下:
import { useEffect, useRef } from "react";function MyInput(props) {return <input {...props} />;
}export default function Example() {const inputRef = useRef(null);useEffect(() => {inputRef.current?.focus();}, []);return <MyInput ref={inputRef} />;
}
我们创建一个 MyInput 子组件,然后把 ref 绑定到我们子组件上。
控制台直接给我们弹了报错:

React 向控制台打印一条错误消息,提示我们如果想操控子组件,需要去使用 forwardRef API。
forwardRef 的用法
forwardRef表示允许子组件将其DOM节点放入ref中,默认情况下是不允许的。forwardRef会让传入的子组件多一个ref作为第二个参数传入,用于存储当前DOM节点,第一个参数为props。
我们改进下:
const MyInput = forwardRef((props, ref) => {return <input {...props} ref={ref} />;
});
我们将 ref 绑定到 MyInput 组件中的 input,再来看看效果:

现在不会报错了,并且 input 框也正常 聚焦 了。
总结
useRef用于存储值且不想去触发render的场景。useRef创建的值通过.current去进行 读取、修改。- 常见用于存储 定时器、
DOM节点。 - 存储自定义组件的
DOM节点需配合forwardRefAPI 使用。 - 需要注意 不要在渲染过程中读取或写入
ref.current。
如果你也是从 Vue 转过来的,看到这里可能已经对 useRef 有了更清晰的认知 —— 它并不是 Vue 的 ref 替代品,而是一种完全不同思路下的状态管理补充工具。
希望这篇文章能帮你快速掌握 useRef,如果你觉得有帮助,别忘了点个赞👍或关注我后续的 重学 React 系列!
相关文章:
Vue 人看 React useRef:它不只是替代 ref
如果你是从 Vue 转到 React 的开发者,初见 useRef 可能会想:这不就是 React 版的 ref 吗?但真相是 —— 它能做的,比你想象得多得多。 👀 Vue 人初见 useRef 在 Vue 中,ref 是我们访问 DOM 或响应式数据的…...
零成本自建企业级SD-WAN!用Panabit手搓iWAN实战
我们前面提到过,最开始了解到Panabit,是因为他的SD-WAN产品(误以为是外国货?这家国产SD-WAN神器竟能免费白嫖,附Panabit免费版体验全记录);现在发现,其SD-WAN的技术基础是iWAN&#…...
Unity-微信截图功能简单复刻-03绘制空心矩形
思路-绘制空心矩形 拓展UGUI的Graphic类,实现拖拽接口。 开始拖拽时记录鼠标位置, 使用拖拽中的鼠标位置和记录的位置,计算矩形顶点,绘制矩形。 两个三角形合并为一个矩形,作为空心矩形的一条边,四个边合并为空心矩形…...
国产品牌芯洲科技100V降压芯片系列
SCT2A25采用带集成环路补偿的恒导通时间(COT)模式控制,大大简化了转换器的片外配置。SCT2A25具有典型的140uA低静态电流,采用脉冲频率调制(PFM)模式,它使转换器在轻载或空载条件下实现高转换效率。 芯洲科技100V降压芯片系列提供丰富的48V系…...
研一自救指南 - 07. CSS面向面试学习
最近的前端面试多多少少都会遇到css的提问,感觉还是要把重点内容记记背背。这里基于b站和我自己面试的情况整理。 20250418更新: 1. BFC Block Formatting Context,一个块级的盒子,可以创建多个。里面有很多个块,他们…...
图灵奖得主LeCun:DeepSeek开源在产品层是一种竞争,但在基础方法层更像是一种合作;新一代AI将情感化
图片来源:This is World 来源 | Z Potential Z Highlights: 新型的AI系统是以深度学习为基础,能够理解物理世界并且拥有记忆、推理和规划能力的。一旦成功构建这样的系统,它们可能会有类似情感的反应,但这些情感是基…...
从GET到POST:HTTP请求的攻防实战与CTF挑战解析
初探HTTP请求:当浏览器遇见服务器 基础协议差异可视化 # 典型GET请求 GET /login.php?username=admin&password=p@ssw0rd HTTP/1.1 Host: example.com User-Agent: Mozilla/5.0# 典型POST请求 POST /login.php HTTP/1.1 Host: example.com Content-Type: application/x…...
SQL-exists和in核心区别、 性能对比、适用场景
EXISTS和IN的基本区别。IN用于检查某个值是否在子查询返回的结果集中,而EXISTS用于检查子 查询是否至少返回了一行数据。通常来说,EXISTS在子查询结果集较大时表现更好,因为一旦找 到匹配项就会停止搜索,而IN则需要遍历整个结果集。 在 SQL 中,EXISTS 和 IN 都可以用于…...
Charles 安装与使用详解:实现 App 与小程序 HTTPS 抓包
在日常的移动端开发、接口调试或逆向分析中,我们经常需要抓取移动 App 或微信小程序的 HTTP/HTTPS 请求。Charles 是一款经典强大的代理抓包工具,凭借简单的界面和强大的功能,成为了 macOS 抓包的首选工具之一。 本文将详细介绍 Charles 的安…...
使用Redis5.X部署一个集群
文章目录 1.用Redis5.x来创建Cluste2. 查看节点信息 nodes3. 添加节点 add-node4.删除节点 del-node5.手动指定从节点 replicate6.检查集群健康状态 check 建议使用5.x版本。 首先,下载Redis,根据自己的环境选择版本。 一键启动Redis集群文件配置。 ech…...
Ubuntu Linux 中文输入法默认使用英文标点
ubuntu从wayland切换到x11, 然后安装fcitx(是fcitx4版本)和 fcitx-googlepinyin, 再sudo dpkg -i 安装百度输入法deb包. 在fcitx配置中, 附加组件,打勾高级, 取消打勾标点支持和全角字符. 百度输入法就可以默认用英文标点了. 而google拼音输入法的问题是字体大小没法保存,每…...
Mermaid 是什么,为什么适合AI模型和markdown
什么是 Mermaid? Mermaid 是一个基于 JavaScript 的开源绘图和图表工具,允许用户通过简单的文本语法创建图表。它支持生成流程图、时序图、类图、甘特图等多种类型的可视化内容,并直接从类似 Markdown 的代码中渲染。Mermaid 因其与 Markdow…...
Java漏洞原理与实战
一、基本概念 1、序列化与反序列化 (1)序列化:将对象写入IO流中,ObjectOutputStream类的writeobject()方法可以实现序列化 (2)反序列化:从IO流中恢复对象,ObjectinputStream类的readObject()方法用于反序列化 (3)意义:序列化机制允许将实现序列化的J…...
第十届团体程序设计天梯赛-上理赛点随笔
2025.4.19来到军工路580号上海理工大学赛点参加cccc 校内环境挺好的,校内氛围也不错;临走前还用晚餐券顺走一袋橘子 再来说说比赛 首先是举办方服务器爆了,导致前10分钟刷不出题,一个多小时还上交不了代码 然后就是我用py总有几…...
考公:数字推理
文章目录 1.真题12 312 530 756 ()-3 3 1 12 17 ()356 342 333 324 ()30 28 27 25 () 2215105 1494 1383 1272 ()2 3 8 21 46 ()4/25 1/4 4/9 1 ()39 416 630 848 ()5 8 11 17 () 10714 21 40 77 () 229 2.数字推理方法2.1 差值法2.2 比值法(乘法关系)2.…...
树莓派超全系列教程文档--(32)config.txt常用音频配置
config.txt常用音频配置 板载模拟音频(3.5mm耳机插孔)audio_pwm_modedisable_audio_ditherenable_audio_ditherpwm_sample_bits HDMI音频 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 板载模拟音频(3.5mm耳机…...
面试专栏-02-MySQL知识点(第二部分)
6、锁 1、分类: 全局锁:锁住数据库中的所有表表级锁:每次操作锁住整张表行级锁:每次操作锁住对应行的数据 2、全局锁 加锁后,整个实例只能进行读取操作,从而保证数据的完成性和一致性。 特点ÿ…...
55、⾸屏加载⽩屏怎么进⾏优化
答: (1)使⽤CDN 减⼩代码体积,加快请求速度; (2)SSR通过服务端把所有数据全部渲染完成再返回给客⼾端; (3) 路由懒加载,当⽤⼾访问的时候,再加载相应模块; (4) 使⽤外…...
python函数之间嵌套使用yield
假设一种场景,函数 A 可以在获得函数 B 的返回值(即一个生成器对象)后,再次对其进行 yield 操作。这是因为 Python 的生成器是可迭代的,你可以在一个生成器中迭代另一个生成器,并将其结果逐个 yield 出去。…...
【MySQL数据库入门到精通】
文章目录 一、SQL分类二、DDL-数据库操作1.查询2.创建数据库3.删除数据库4.使用数据库 三、DDL-表操作1.查询 一、SQL分类 根据功能主要分为DDL DML DQL DCL DDL:Date Definition Language数据定义语言:定义数据库,表和字段 DML:Date Manipulatin Lan…...
[Swift]pod install成功后运行项目报错问题error: Sandbox: bash(84760) deny(1)
操作: platform :ios, 14.0target ZKMKAPP do# Comment the next line if you dont want to use dynamic frameworksuse_frameworks!# Pods for ZKMKAPPpod Moyaend pod install成功后运行报错 报错: error: Sandbox: bash(84760) deny(1) file-writ…...
游戏引擎学习第233天
原地归并排序地方很蒙圈 game_render_group.cpp:注意当前的SortEntries函数是O(n^2),并引入一个提前退出的条件 其实我们不太讨论这些话题,因为我并没有深入研究过计算机科学,所以我也没有太多内容可以分享。但希望在过去几天里…...
卷积神经网络基础(二)
停更好久的卷积神经网络基础知识终于开始更新了哈哈,今天主要介绍的是误差反向传播法。 目录 一、计算图 1.1 用计算图求解 1.2 局部计算 1.3 为什么采用计算图 二、链式法则 2.1 计算图的反向传播 2.2 链式法则 2.3 链式法则和计算图 三、反向传播 3.1 …...
探索大语言模型(LLM):定义、发展、构建与应用
文章目录 引言大规模语言模型的基本概念大规模语言模型的发展历程1. 基础模型阶段(2018年至2021年)2. 能力探索阶段(2019年至2022年)3. 突破发展阶段(以2022年11月ChatGPT的发布为起点) 大规模语言模型的构…...
树莓派超全系列教程文档--(33)树莓派启动选项
树莓派启动选项 启动选项start_file ,fixup_filecmdlinekernelarm_64bitramfsfileramfsaddrinitramfsauto_initramfsdisable_poe_fandisable_splashenable_uartforce_eeprom_reados_prefixotg_mode (仅限Raspberry Pi 4)overlay_prefix配置属…...
PTA:模拟EXCEL排序
Excel可以对一组纪录按任意指定列排序。现请编写程序实现类似功能。 输入格式: 输入的第一行包含两个正整数 n (≤105) 和 c,其中 n 是纪录的条数,c 是指定排序的列号。之后有 n 行,每行包含一条学生纪录。每条学生纪录由学号(6…...
Python 爬虫解决 GBK乱码问题
文章目录 前言爬取初尝试与乱码问题编码知识科普UTF - 8GBKUnicode Python中的编码转换其他编码补充知识GBKGB18030GB2312UTF(UCS Transfer Format)Unicode 总结 前言 在Python爬虫的过程中,我尝试爬取一本小说,遇到GBK乱码问题&a…...
Scala与人工智能:融合多范式编程的AI开发利器
在人工智能(AI)技术飞速发展的今天,编程语言的选择直接影响着算法实现效率与系统可扩展性。Scala,作为一门融合面向对象(OOP)与函数式编程(FP)的多范式语言,凭借其独特的…...
解决echarts饼图label显示不全的问题
解决办法 添加如下配置: labelLayout: {hideOverlap: false},...
JCST 2025年 区块链论文 录用汇总
Conference:Journal of Computer Science and Technology (JCST) CCF level:CCF B Categories:交叉/综合/新兴 Year:2025(截止4.19) JCST 2024年 区块链论文 录用汇总 1 Title: An Understandable Cro…...
