2026/5/24 5:49:54
网站建设
项目流程
怎么样签约设计网站,wordpress页面后台登录,如何制作网页效果图,成都 广告公司网站建设在 React 的学习过程中#xff0c;你一定遇到过这种困境#xff1a;
我想操作 DOM#xff08;比如让输入框自动聚焦#xff09;#xff0c;但 React 告诉我不要直接操作 DOM。我想保存一个变量#xff0c;不希望它重置#xff0c;但也不希望它的改变触发组件重新渲染。我…在 React 的学习过程中你一定遇到过这种困境我想操作 DOM比如让输入框自动聚焦但 React 告诉我不要直接操作 DOM。我想保存一个变量不希望它重置但也不希望它的改变触发组件重新渲染。我在useEffect里怎么都拿不到最新的 State 值闭包陷阱。这时候你需要的就是useRef。一、 什么是 useRef简单来说useRef创建了一个普通的 JavaScript 对象它长这样{ current: ... // 这里存着你的值 }它有两个核心特性必须死记硬背引用透传在组件的整个生命周期内这个对象永远是同一个引用地址不变。变更不渲染修改ref.current的值不会触发组件重新渲染这与useState完全相反。打个比方useState像是橱窗里的模特。换了衣服状态改变大家都能看见页面重绘。useRef像是你口袋里的记事本。你写了什么修改值只有你自己知道外面的人看不见页面不会重绘。二、 场景一访问 DOM 节点最常见用法React 是声明式的我们通常不需要直接碰触 DOM。但在某些场景下管理焦点、文本选择、媒体播放、强制动画等我们需要“逃生舱”。import { useRef, useEffect } from react; export default function TextInputWithFocusButton() { // 1. 创建一个 ref初始值为 null const inputEl useRef(null); const onButtonClick () { // 3. 通过 .current 访问真实的 DOM 节点 // 注意React 会在组件挂载后自动把 DOM 赋给 current inputEl.current.focus(); console.log(输入框现在的宽度是:, inputEl.current.offsetWidth); }; return ( div {/* 2. 把 ref 绑定到 JSX 元素上 */} input ref{inputEl} typetext / button onClick{onButtonClick}点我聚焦输入框/button /div ); }注意不要过度使用 Ref 操作 DOM。如果你发现自己在用 Ref 去修改 DOM 的内容如inputEl.current.value hello这通常意味着你写错了应该用useState控制。三、 场景二存储“幕后”变量解决闭包陷阱还记得上一篇文章里的“闭包陷阱”吗定时器里永远只能读到旧的 count。除了把 count 加入依赖项useRef 提供了一种更巧妙的解法“逃课”大法。既然闭包锁住的是变量的引用那我们就创建一个永远不变的容器Ref把最新的值随时放进去。import { useState, useEffect, useRef } from react; export default function Timer() { const [count, setCount] useState(0); // 1. 创建一个 Ref 用来“偷运”最新的 count const latestCountRef useRef(count); // 2. 每次渲染都把最新的 count 写入 Ref // 这不会触发重绘因为修改 Ref 是副作用 latestCountRef.current count; useEffect(() { const timer setInterval(() { // 3. 定时器里不读 State而是读 Ref // 因为 Ref 对象的引用地址从未改变所以闭包能一直访问到它 // 而 .current 属性总是被我们要么更新为最新的 console.log(定时器读取到的最新值:, latestCountRef.current); }, 1000); return () clearInterval(timer); }, []); // ✅ 依赖项可以为空因为 Ref 对象本身是稳定的 return ( div pCount: {count}/p button onClick{() setCount(count 1)}1/button /div ); }原理解析count是每次渲染都不同的数字值类型。闭包一旦形成捕获的是当年的那个数字。latestCountRef是一个对象引用类型。闭包捕获的是这个对象的地址。哪怕里面的.current变了只要地址没变闭包就能顺藤摸瓜找到最新的值。四、 场景三记录“上一次”的值有时候我们需要知道状态“上一次”是什么比如判断股票是涨了还是跌了。React 没有提供usePrevious这样的 Hook我们可以用useRef自己造一个。import { useState, useEffect, useRef } from react; function Counter() { const [count, setCount] useState(0); // 用于存储上一次的值 const prevCountRef useRef(); useEffect(() { // 渲染完成后更新 ref // 只有在下一次渲染时我们才能拿出来对比 prevCountRef.current count; }); // 每次渲染后都执行 // 在本次渲染中prevCountRef.current 还是旧值 // 因为 useEffect 是在渲染结构提交到屏幕**之后**才运行的 const prevCount prevCountRef.current; return ( div h1当前: {count}/h1 h2上一次: {prevCount}/h2 button onClick{() setCount(count 1)}1/button /div ); }五、 灵魂拷问为什么不用普通变量新手常问“为什么不用let variable 0定义在组件外面或者里面非要用useRef”1. 为什么不能定义在组件里面function App() { let timerId 0; // ❌ 错误 // ... }原因组件每次重新渲染函数就会重新执行。timerId会被重置为 0。你辛辛苦苦存的数据瞬间丢失。2. 为什么不能定义在组件外面let timerId 0; // ❌ 错误除非是单例 function App() { // ... }原因 如果你的页面上有 5 个 组件它们会共享同一个全局的 timerId。一个组件改了别的组件全乱套了。useRef 保证了数据是“也就是组件实例独享的”且“穿越渲染周期而不丢失”。总结useRef vs useState特性useStateuseRef主要用途存储直接影响视图的数据存储 DOM 引用、定时器 ID、无关视图的数据数据变化时触发组件重新渲染不触发重新渲染读取时机渲染过程中直接读取通常在 useEffect 或事件处理函数中读取心智模型组件的状态State组件的实例变量Instance Variable当你下一次想在 React 里存点东西但又不想因为它变了而导致页面莫名其妙闪烁重绘时请立刻想起useRef