React useRef 与 DOM

React useRef 与 DOM

前置:React 常用 Hooks

目标:会用 useRef 访问 DOM;理解「改 ref.current 不会重渲染」。

引言

useState 一改,组件就重画;但有些东西你只想记住,不想因为记住它而再渲染一遍:

  • 输入框的 DOM 节点,好调用 .focus() / .select()
  • setInterval 返回的 timer id,卸载时清掉
  • 「上一次」的 props、渲染次数等调试信息

这就是 useRef :给你一个在多次渲染之间一直活着的可变盒子 { current: ... }。

改 current 不会触发重渲染 ------这是它和 useState 最大的差别。

本篇 demo:进页自动聚焦输入框、「全选文字」、用 ref 数渲染次数。

动手

完整代码:react2/10-useref-dom/demo

bash 复制代码
cd react2/10-useref-dom/demo
npm install && npm run dev

打开后光标应已在输入框里;输入几个字,点「全选文字」;看下方「渲染次数」随输入变多(靠 ref 计数,不是 state)。


1. useRef 是什么

tsx 复制代码
const inputRef = useRef<HTMLInputElement>(null)
const renderCount = useRef(0)
含义
返回值 对象 { current: 初始值 },组件生命周期内同一引用
改 ref.current 立刻变,不调度重渲染
常见初始值 DOM:null;计数 / id:0 / undefined

TypeScript 里 DOM ref 常写成 useRef<HTMLInputElement>(null),用之前用可选链:inputRef.current?.focus()。


2. 用途一:DOM 句柄

把 ref 挂到元素上,渲染后 current 就是真实 DOM:

tsx 复制代码
const inputRef = useRef<HTMLInputElement>(null)

useEffect(() => {
  inputRef.current?.focus()
}, [])

return (
  <>
    <input
      ref={inputRef}
      value={text}
      onChange={(e) => setText(e.target.value)}
      placeholder="打开页面就会 focus"
    />
    <button type="button" onClick={() => inputRef.current?.select()}>
      全选文字
    </button>
  </>
)

要点:

  • ref={inputRef} :React 把节点写进 inputRef.current
  • 聚焦放进 useEffect :首屏 commit 之后 DOM 才就绪;在 render 函数体里直接 focus() 时机不对
  • 命令式 API (focus / select / scrollIntoView)适合用 ref;「显示什么字」仍用 value + useState

3. 用途二:可变盒子(不驱动 UI)

demo 用 ref 统计渲染次数:

tsx 复制代码
const renderCount = useRef(0)
renderCount.current += 1

每次渲染都会 += 1,但不会因为这一行再引发一次渲染 。

页面上能看到数字变大,是因为你改了 text(state)引起的重渲染,顺便把最新的 renderCount.current 画出来。

其它典型用法:

tsx 复制代码
// 存定时器 id,卸载时清理
const timerRef = useRef<number>()
useEffect(() => {
  timerRef.current = window.setInterval(() => { /* ... */ }, 1000)
  return () => clearInterval(timerRef.current)
}, [])

// 记住上一次的值(对比 props / 做动画)
const prevText = useRef(text)
useEffect(() => {
  prevText.current = text
}, [text])

4. useRef vs useState

useState useRef
改了以后 触发重渲染,UI 更新 不重渲染
适合 要上屏的数据:输入内容、开关、列表 DOM 句柄、timer id、与渲染无关的缓存
读取 const [x, setX] = ... ref.current

别用 ref 代替 state 驱动界面。

例如把输入内容只写在 ref.current 里、不 setState:界面不会跟着变,受控输入也会怪。

口诀:

  • 要显示、要随用户操作更新 UI → useState
  • 要抓住 DOM / 记住一个不需要上屏的值 → useRef

5. 对照表

概念 干什么 demo
useRef(null) + ref={...} 拿 DOM 输入框 inputRef
useEffect + focus() 挂载后命令式操作 自动聚焦
current?.select() 按钮里操作 DOM 「全选文字」
useRef(0) 自增 跨渲染计数且不触发渲染 renderCount
useState 管 text 真正驱动输入显示 受控输入框

6. 常见坑

做法 问题
在 render 里直接 inputRef.current.focus() 首次渲染时往往还是 null,时机不稳
用 useState 存 timer id 只为了清理 能跑,但会多一次无意义渲染;用 ref 更合适
把要展示的计数放进 ref,却期望改它就刷新 UI 不会刷新;该上屏就用 state
忘了 ?. current 可能为 null(未挂载 / 条件渲染卸掉)
把 ref 对象本身当依赖:useEffect(..., [inputRef]) ref 对象身份稳定,几乎等于没依赖;一般依赖业务数据,DOM 操作放 effect 内读 current

demo 在练什么

  1. ref 挂到 <input>,挂载后 focus
  2. 按钮里用 select() 做命令式 DOM 操作
  3. 用 ref 计数渲染次数,体会「改 current ≠ 重渲染」;真正带动重渲染的是 text 的 state

自己改一改:加一个「清空并聚焦」按钮;或用 ref 存 setTimeout id,在输入防抖里练习清理。


下一篇

React Context 跨层传值

相关推荐
JudithHuang1 小时前
React useReducer 复杂状态
react.js
JudithHuang1 小时前
React 组件与 Props
react.js
JudithHuang1 小时前
React:第一个 Vite 应用
react.js
JudithHuang1 小时前
React State 与事件
react.js
JudithHuang1 小时前
React 大型项目目录骨架
react.js
JudithHuang1 小时前
React 条件渲染与 JSX 进阶
react.js
JudithHuang2 小时前
React 常用 Hooks
前端·react.js·前端框架