CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
React 核心/课时/useRef 与 DOM
RawBlame
课时目录14 节

React 核心

  1. 01React 是什么
  2. 02JSX 与渲染
  3. 03函数组件
  4. 04Props 与 State
  5. 05事件处理
  6. 06条件渲染与列表
  7. 07useEffect
  8. 08useRef 与 DOM
  9. 09useMemo、useCallback 与自定义 Hook
  10. 10表单与受控组件
  11. 11Context 共享状态
  12. 12React Router 入门
  13. 13数据获取与错误边界
  14. 14综合练习与 Next.js 方向

useRef 与 DOM

引用 DOM、保存可变值不触发渲染。

25 分钟

useRef 与 DOM

这一课你将能做到

  1. 说明 ref 与 state 的差别(会不会触发重渲染)
  2. 用 ref 拿到 input 并在按钮点击时 focus()
  3. 知道什么时候不该用 ref 硬改 UI

为什么要学

有时你需要「记住一个值但不想因为这个值重画界面」,或「拿到真正的 DOM 节点」(聚焦、测宽、播视频)。
这时用 useRef。

人话:ref 是一个可变盒子,改盒子里的东西默认不会让 React 重画页面。
本站搜索框「一点按钮光标进输入框」、音视频控件,都是 ref 的典型活。显示在屏幕上的字仍然用 state。

先建立图像

state:变了 → 重新渲染 → 用户看到变化
ref.current:变了 → 默认不重新渲染(只是盒子里的值换了)
需求用什么
屏幕上要显示的数字/文案state
聚焦输入框、读 video 当前时间ref
记「上一次 count」给逻辑用常也能用 ref
菜单开没开state(要重画)

跟着做

步骤 1:聚焦输入框(完整可抄)

import { useRef } from "react";

export function FocusDemo() {
  const inputRef = useRef(null);

  return (
    <>
      <input ref={inputRef} placeholder="点按钮后我会被聚焦" />
      <button type="button" onClick={() => inputRef.current?.focus()}>
        聚焦
      </button>
    </>
  );
}

ref={inputRef} 让 React 把 DOM 节点填进 inputRef.current。
第一次渲染刚结束前,current 可能还是 null,所以用可选链 ?.。

步骤 2:存「上一次的值」(混个脸熟)

import { useEffect, useRef } from "react";

export function PrevCount({ count }) {
  const prev = useRef(count);
  useEffect(() => {
    prev.current = count;
  }, [count]);
  return (
    <p>
      这次 {count},上次 {prev.current}
    </p>
  );
}

进阶:比较这次和上次而不额外为「上次」开一个会触发渲染的 state。先知道有这用法即可。

步骤 3:想清楚边界

能声明式解决就少操作 DOM:

  • 显示/隐藏:条件渲染,不要 ref.current.style.display = "none"
  • 按钮文字:state,不要 ref.current.textContent = ...
  • 列表:map,不要 innerHTML

ref 是逃生舱,不是默认方向盘。

步骤 4:和 getElementById 比

在 React 树内优先用 ref。getElementById 可能拿到别的节点、过期节点,或在 SSR 时根本没有 document。

步骤 5:测宽(只读 DOM)

useEffect(() => { const w = boxRef.current?.getBoundingClientRect().width; console.log(w); }, []);

读布局信息适合 ref;不要把测量结果每帧 setState 除非真正要显示。

讲清楚要点

  1. ref.current 可变,读写都在事件或 Effect 里较安全。
  2. 渲染期间不要靠写 ref.current 驱动显示——显示仍用 state。
  3. 能声明式解决就少操作 DOM。
  4. 与 state 选型:用户看得见 → state;握节点/记隐式值 → ref。
  5. 给组件传 ref 需要 forwardRef 或 React 19 的 ref 作为 props——入门先会绑原生标签。

你可能会懵的地方

  • ?. 是什么?
    可选链:current 可能暂时是 null,没有节点就跳过调用。

  • 改 ref 为何界面不动?
    这是设计如此。要动界面,请 setState。

  • useRef(0) 和 useState(0) 都能记数字?
    都能。但只有 state 变了会重画。计时器 tick 若每秒 setState,界面闪;若只内部计数、不显示,用 ref。

  • 渲染函数里读 ref.current 显示出来?
    改 current 不会触发渲染,你读到的可能过时。要显示就用 state。

  • SSR 时 ref.current?
    服务端没有 DOM,current 为 null;测量放 Effect。

错法对照

错法问题正解
用 ref 代替所有 stateUI 不更新要显示的用 state
渲染函数里读写 DOM难预测、易打架事件 / Effect 里操作
用 ref 存「是否展开」却不设 state菜单永远不重画展开状态用 state
getElementById 满天飞和 React 脱节树上用 ref
用 ref 改文案代替 state与虚拟 DOM 脱节文案用 state
渲染期写 ref.current = 驱动 UI难预测事件/Effect

自测三问

  1. 改 ref.current 会重渲染吗?
  2. 聚焦输入框为什么适合 ref?
  3. 什么 UI 变化应该用 state 而不是 ref?

小结一句话

ref 是不触发渲染的可变盒,适合握 DOM 或记值;真正要显示的变化仍用 state。

练习:给搜索框加一个「聚焦」按钮,只用 ref 调 focus(),不要用 getElementById。界面上的占位文案仍写在 JSX 里,不要用 ref 改 placeholder。

动手加深

搜索框:按钮聚焦 input;占位符仍写在 JSX。完成后口述 state/ref 边界。

跟着做(加厚 · react-refs)

步骤:最小可运行对照

在 Vite 或 Next 练习页粘贴本课核心示例,改一个输入,确认屏幕变化符合预期。再故意写一个错法表里的写法,看控制台/界面如何坏,然后改回。

每个概念都问:state 在谁手里?UI 哪一块会因它重绘?

操作顺序:打开对应文件 → 改一处可观察点(文案、类型、className 或分支)→ 保存看结果 → 复原或留下有意义的小改动。

步骤:错法故意练一次

先按「用 jQuery 式直接改 DOM 冒充完成」做 30 秒(或在注释里写出若这样做会怎样),再改成「改数据(state/props)让 UI 推导出来」。 目的不是留坑,而是让失败现象进长期记忆。

你可能会懵的地方(加厚)

  • 热更新后状态丢了?
    正常:整树重挂会重置 state。用 key 或外提状态保留;开发期别被吓到。

  • 和文档示例不一致?
    以你锁的 React 大版本 + react.dev 为准;旧博客的 class 组件示例仅作历史阅读。

  • 这课和前后课怎么接?
    回看大纲上一课的小结一句、下一课标题;用自己的话写「本课产出什么可观察结果」。写不出来就重做跟着做。

错法对照(加厚)

错法问题正解
用 jQuery 式直接改 DOM 冒充完成假完成/埋雷改数据(state/props)让 UI 推导出来
只看不敲第二天全忘至少跑通本课步骤
跳过自测三问以为会了闭卷答再对稿

延伸

  • react.dev Referencing Values with Refs
  • 下一课:useMemo / useCallback / 自定义 Hook。
加载本节练习…
← 上一课useEffect下一课 →useMemo、useCallback 与自定义 Hook