React useRef 实战:从 input 聚焦到 Web Worker 引用

React useRef 实战:从 input 聚焦到 Web Worker 引用

本文基于本地 React ref-demo 静态源码整理,代码部分运行未验证。文章重点解决两个初学者问题:useRef 绑定 DOM 后发生什么,以及为什么 Worker 实例适合保存到 ref 中。

标签:React, React Hooks, useRef, Web Worker

一、先建立最小理解

React 中的 useRef 不只是"获取 DOM"的工具。它返回一个稳定对象,对象的值放在 current 属性中。React 会在多次渲染之间保留这个对象,但修改 current 不会自动触发页面重新渲染。

这组练习包含两个场景:第一个场景把 ref 绑定到 input,并在组件挂载后调用 focus();第二个场景把 Web Worker 实例放进 ref,避免组件重新渲染时重复创建线程。

二、useRef 到底保存了什么?

jsx 复制代码
const inputRef = useRef(null)

可以先把它理解成:

js 复制代码
{
  current: null
}

初始值是 null,因为组件刚执行时,input 还没有挂载到真实 DOM。绑定之后:

jsx 复制代码
<input ref={inputRef} placeholder="请输入用户名" />

React 会在 input 挂载后,让:

js 复制代码
inputRef.current

指向真实的 HTMLInputElement 对象。此时就可以调用:

js 复制代码
inputRef.current.focus()
inputRef.current.value
inputRef.current.blur()

这里需要注意:ref={inputRef} 是 JSX 属性,不是普通字符串。React 负责在挂载和卸载阶段维护 current 的引用。

三、为什么 DOM 操作要放在 useEffect?

项目中的聚焦逻辑是:

jsx 复制代码
const inputRef = useRef(null)

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

执行顺序可以简化为:

text 复制代码
执行组件函数
  ↓
current 初始为 null
  ↓
React 创建并挂载 input
  ↓
current 指向 input DOM
  ↓
useEffect 执行 focus()

因此,如果在组件函数执行阶段直接调用 inputRef.current.focus(),可能因为 current 还是 null 而无法操作 DOM。

如果只是首次加载时聚焦,也可以写:

jsx 复制代码
<input autoFocus placeholder="请输入用户名" />

区别在于:autoFocus 是声明式的初次聚焦;ref 可以在按钮点击、弹窗打开或异步请求结束后动态调用 focus()

四、useRef 和 useState 如何选择?

项目中还有一个保存数字的练习:

jsx 复制代码
const numRef = useRef(0)
const [, forceRender] = useState(0)

<div onClick={() => numRef.current++ + forceRender()}>
  <h1>{numRef.current}</h1>
</div>

点击时,numRef.current++ 修改了 ref,但它本身不会让页面更新;forceRender() 才触发了重新渲染,所以新的数字才会显示出来。

这个例子适合用来理解机制,不推荐作为实际业务写法。判断标准如下:

数据特点 推荐方案
改变后需要更新页面 useState
DOM 节点引用 useRef
Worker、定时器或 WebSocket 实例 useRef
需要展示的计算结果 useState

一句话:State 管页面,Ref 管引用。

五、Web Worker 是怎么创建的?

项目中创建 Worker 的代码是:

jsx 复制代码
const workerRef = useRef(null)

useEffect(() => {
  workerRef.current = new Worker(
    new URL('./worker.js', import.meta.url)
  )
}, [])

真正创建新线程的是:

js 复制代码
new Worker(...)

并不是 useRefuseRef 只负责保存 Worker 实例,方便后续调用:

js 复制代码
workerRef.current.postMessage(data)
workerRef.current.terminate()

new URL 的含义

js 复制代码
new URL('./worker.js', import.meta.url)

它表示:以当前模块文件的地址为基准,定位同目录或相对目录中的 worker.js。在 Vite 项目中,这种写法可以让构建工具处理 Worker 文件路径。

当前项目的 worker.js 只有一条启动日志:

js 复制代码
console.log('worker 线程启动了')

因此,当前材料只证明了 Worker 的创建意图,尚未形成主线程与 Worker 之间的消息通信闭环。

六、Worker 为什么要配合 useEffect 和清理?

不要在组件函数中直接创建:

jsx 复制代码
function App() {
  const worker = new Worker('./worker.js')
}

组件每次重新渲染时,函数可能再次执行,从而创建多个 Worker。

更完整的资源管理方式是:

jsx 复制代码
useEffect(() => {
  const worker = new Worker(
    new URL('./worker.js', import.meta.url)
  )

  workerRef.current = worker

  return () => {
    worker.terminate()
    workerRef.current = null
  }
}, [])

useEffect 创建外部资源,返回的清理函数释放外部资源。这个模式也适用于定时器、事件监听和 WebSocket。

七、常见错误排查表

现象 原因 排查方式
currentnull DOM 尚未挂载或没有绑定 ref 检查 ref={inputRef},并在 effect 或事件中访问
修改 ref 后页面不变 ref 不会触发重新渲染 页面状态改用 useState
每次渲染都创建 Worker 在组件函数中创建了 Worker 放进 useEffect
Worker 无法加载 路径或构建方式不匹配 检查 new URL 的相对路径
页面关闭后 Worker 仍存在 没有清理线程 卸载时调用 terminate()
Worker 没有返回结果 未实现消息监听和发送 补充 onmessagepostMessage

八、面试中如何介绍这个练习

我通过 React useRef 学习了两类非响应式引用:一类是通过 ref 获取 input DOM 并在 effect 中调用 focus(),另一类是保存 Web Worker 实例。这里 new Worker 才负责创建线程,ref 只负责跨渲染保存实例;需要展示给用户的计算结果则应该放入 state。资源型对象还要在组件卸载时清理,避免留下 Worker 实例。

九、最后的自检清单

  • 能解释 useRef(null) 返回的对象结构。
  • 能说明 input 挂载前后 current 的变化。
  • 能区分 autoFocusref.current.focus()
  • 能说明为什么页面状态使用 useState
  • 能解释 new WorkeruseRef 各自的职责。
  • 能解释 new URL('./worker.js', import.meta.url)
  • 能写出 Worker 的创建和 terminate() 清理流程。
  • 能指出当前 worker.js 还没有实现消息通信。
相关推荐
苏灿烤鱼1 小时前
GitHub #2 拆解|把工程经验装进 Agent,为什么仍会“静默失效”?
javascript·人工智能·agent
90后的晨仔1 小时前
uni-app 生命周期深度解析(iOS / Android / 鸿蒙 / Vue3 四端对照)
前端
钛态3 小时前
AI 辅助:前端框架反模式:过度封装、状态滥用与副作用失控
前端·vue·react·web
Rysxt_3 小时前
React vs Vue.js:2025年两大主流前端框架深度对比教程
vue.js·react.js·前端框架
by__csdn3 小时前
Vue vs React vs Angular:前端框架终极对决
前端·vue.js·react.js·前端框架·vue·react·angular
钛态3 小时前
前端框架选型决策框架:React、Vue、Svelte、Solid 的 2026 年横评
前端·vue·react·web
2501_926102863 小时前
深入浅出Vue.js前端框架设计:核心原理与实践指南
前端·vue.js·前端框架
程序员黑豆4 小时前
使用AI编程开发鸿蒙应用:从环境搭建到实战示例
前端·harmonyos
程序员黑豆8 小时前
鸿蒙应用开发:一次开发多端部署之响应式布局完全指南
前端·harmonyos