踩坑实录:把 useRef 写进 JSX 后,我终于分清它和 useState 的核心差异

作为 React 新手,刚接触 Hooks 时,很容易混淆 useState 和 useRef------两者都能存储数据,那到底该怎么选?

我最近通过一个「点击计数」的简单练习,结合一个关键疑问,彻底理清了它们的核心区别。这篇文章就把我的学习过程和理解分享出来,希望能帮到同样困惑的小伙伴。

一、先从一个极简练习示例入手

我最初的需求很简单:做一个点击计数组件,需要满足两个功能:

  1. 显示「当前轮次点击数」,支持重置;
  2. 显示「页面加载后的总点击数」,重置当前轮次后总次数不清零。

结合 useState 和 useRef,最终写出了这样的代码(可直接运行):

ini 复制代码
import { useState, useRef } from 'react';

function ClickCounter() {
  // useState:存储当前轮次点击数(驱动视图更新)
  const [currentCount, setCurrentCount] = useState(0);
  
  // useRef:存储总点击数(持久化保存,修改不触发重渲染)
  const totalCountRef = useRef(0);

  // 点击计数:同时更新两个数据
  const handleClick = () => {
    // 更新当前轮次计数(触发重渲染)
    setCurrentCount(prev => prev + 1);
    
    // 更新总计数(直接修改 current,不触发重渲染)
    totalCountRef.current += 1;
    
    // 控制台打印总计数,验证数据
    console.log('总点击数(ref):', totalCountRef.current);
  };

  // 重置当前轮次计数(总计数不变)
  const handleResetCurrent = () => {
    setCurrentCount(0);
  };

  return (
    <div style={点击计数练习当前轮次点击数:{currentCount}总点击数(持久化):{totalCountRef.current}<button onClick={ }}>
        点击计数
      <button onClick={handleResetCurrent}>
        重置当前计数
      
  );
}

export default function App() {
  return <ClickCounter />;
}

二、我的核心疑问:useRef 不是不能驱动视图吗?为什么能写在 JSX 里?

写完代码后,我立刻产生了一个疑问:

之前学习时知道 useRef 用于存储不需要驱动视图更新的数据,修改 ref.current 不会触发重渲染。但这个示例里,我把 totalCountRef.current 直接写在了 JSX 里,这难道不矛盾吗?这样写到底有没有问题?

这个疑问也让我意识到,很多新手对 useState 和 useRef 的理解,可能都停留在「表面定义」,没有深入到「渲染逻辑」层面。

三、彻底搞懂:useRef 写在 JSX 里到底行不行?

带着疑问,我梳理了核心结论,再通过代码执行过程验证,终于彻底明白:

1. 语法上:完全没问题

React 的 JSX 支持嵌入任意合法的 JavaScript 表达式,totalCountRef.current 本质就是一个普通的 JS 变量(或数值、字符串等),所以把它写在 JSX 里不会报错,属于合法写法。

2. 功能上:能显示但不能「主动驱动更新」

这是最关键的一点------useRef 的值可以显示在页面上,但修改它不会触发组件重渲染,因此页面上的显示值不会「实时更新」,只会显示组件上一次渲染时的旧值。

我们结合示例代码的执行过程,一步步拆解:

步骤 1:组件首次渲染

  • currentCount = 0(useState 初始值);
  • totalCountRef.current = 0(useRef 初始值);
  • JSX 渲染结果:当前轮次点击数:0,总点击数:0。

步骤 2:第一次点击「计数」按钮

  • 执行 setCurrentCount(prev => prev + 1):修改 useState 状态,触发组件重渲染;
  • 执行 totalCountRef.current += 1:totalCountRef.current 变成 1,但这个修改不会触发重渲染;
  • 因为 useState 触发了重渲染,JSX 会重新读取所有值,此时总点击数显示为 1(这里能更新是因为 useState 带动了渲染,不是 useRef 自己驱动的)。

步骤 3:验证「只改 useRef 不触发更新」

为了更直观验证,我加了一个「只修改总计数」的按钮:

xml 复制代码
<button onClick={ totalCountRef.current += 1}>只改总计数

点击这个按钮后:

  • 控制台打印 totalCountRef.current,会发现数值确实增加了;
  • 但页面上的总点击数没有任何变化------因为没有修改 useState 状态,组件没有重渲染,JSX 里的 totalCountRef.current 还是上一次渲染的旧值。

四、useState 与 useRef 的核心差异总结

通过这个示例和疑问,我终于理清了两者的核心区别,用表格总结最清晰:

对比维度 useState useRef
核心作用 存储驱动视图更新的状态 存储持久化、不驱动视图的数据
修改后是否触发重渲染 是(调用 setXxx 必触发) 否(直接改 current 不触发)
数据更新方式 不可变更新(必须用 setXxx) 可变更新(直接改 current)
写在 JSX 里的效果 实时更新显示 不主动更新,需依赖其他逻辑触发渲染
适用场景 页面需要实时显示的数据(计数、表单值、列表等) 定时器 ID、DOM 元素引用、持久化不显示的状态(如总点击数)

五、新手避坑指南

梳理完这些内容后,我总结了几个新手容易踩的坑,供大家参考:

1. 不要用 useRef 存储需要实时显示的数据

如果数据需要在页面上实时更新(比如当前轮次点击数),一定要用 useState。用 useRef 只会导致页面显示滞后。

2. 不要误以为 useRef 不能写在 JSX 里

语法上完全可以写,但要清楚它的局限性------不能主动驱动更新。只有数据不常变化(如固定 ID、初始化后的常量)时,写在 JSX 里才合理。

3. 避免滥用「强制更新」让 useRef 实时显示

如果非要让 useRef 的值实时显示,有人会用「空 state」强制触发渲染:

ini 复制代码
const [, forceUpdate] = useState({});
const handleClick = () => {
  totalCountRef.current += 1;
  forceUpdate({}); // 强制触发渲染
};

这种写法虽然能实现效果,但不推荐------既然需要实时更新,直接用 useState 管理数据更符合 React 设计理念,代码也更简洁。

4. 普通变量无法替代 useRef 做持久化

新手可能会想:"既然 useRef 只是持久化数据,用普通变量不行吗?" 答案是不行------组件每次重渲染时,函数内部的普通变量都会重新初始化(比如 let totalCount = 0 会每次重置为 0),而useRef 的 current 在组件整个生命周期内都会保留数据。

六、总结

其实 useState 和 useRef 的选择逻辑很简单:

如果数据需要驱动视图更新 → 用 useState;

如果数据只需要持久保存、不需要驱动视图 → 用 useRef。

这次通过一个简单的练习示例,加上自己的疑问和梳理,让我对这两个 Hooks 的理解从「表面记忆」变成了「深层理解」。希望我的学习过程能帮到更多新手小伙伴~

如果有疑问或不同看法,欢迎在评论区交流~

相关推荐
鬼手点金21 分钟前
opencode-rules 插件
java·开发语言·前端·人工智能·chrome·git·github
柳杉22 分钟前
借助 Codex 和可视化工作流,从调研到完整实现,3D 大屏竟然可以这么简单
前端·ai编程·数据可视化
IMPYLH1 小时前
HTML 的 <wbr> 元素
前端·html
Dovis(誓平步青云)1 小时前
常用命令藏得太深?怎么做一个快捷命令坞
运维·前端·自动化·音视频·prometheus
IT_陈寒2 小时前
Vite的这个问题坑了我三天,原来如此解决!
前端·人工智能·后端
网易云信2 小时前
大模型能上岗做销售吗?我们让 12 款国产模型交了 420 份作业
前端·后端
蜗牛互联网2 小时前
Python Agent工具调用的白名单、参数校验与审批门禁
java·服务器·前端·人工智能·后端·python
计算机魔术师2 小时前
Perplexity:每个 Project 都是一个 Git 仓库
前端
Andrew5513 小时前
让 AI 写页面不再“一眼假”:我把设计品味做成了一个 Claude Code Skill 流水线
前端
乐享qicheng3 小时前
14-AI智能管家JSBridge深度问答
前端