介绍
在 React
开发中,第一反应往往是用 state
和 props
来驱动界面更新。但也并不是所有需求都需要触发渲染:很多时候,你只是想在组件内部“记住一些东西”或直接操作真实 DOM。
这个时候,useRef
才是更合适的工具。
- 直接获取并操作 DOM(如
focus、scrollIntoView
) - 在渲染之间持久保存可变值而不触发重渲染(如计时器 ID)
- 在多次渲染间保留“上一次”的信息(如上一次的
prop/state
)
什么是 useRef?
useRef
是一个 React Hook,它提供一个“可变对象”,更新它不会触发组件重新渲染。
const myRef = useRef(initialValue);
特性
- 返回一个形如
{ current: value }
的对象 - 可通过
myRef.current = newValue
更新值 - 值在渲染期间保持引用不变(稳定)
- 改变
current
不会导致重新渲染
useRef 用于访问 DOM 元素
最常见的用例:获取 DOM 节点并调用原生方法。
例如,组件挂载时自动聚焦输入框:
import React, { useRef, useEffect } from "react";
function SearchInput() {
const inputRef = useRef();
useEffect(() => {
inputRef.current.focus(); // Focus the input on mount
}, []);
return <input ref={inputRef} placeholder="Type to search..." />;
}
发生了什么?
inputRef.current
指向真实的 DOM 元素- 可直接调用
.focus()
、.scrollIntoView()
等原生 API
这在以下场景尤为常见:
- 登录/注册表单
- 模态框首次打开时聚焦
- 聊天输入框
- 搜索栏与命令面板
useRef 存储可变状态(不重新渲染)
当你需要“记住一个值”,但又不希望它影响渲染时,useRef
是更合适的容器。
以下示例使用 setInterval
构建计时器:
import React, { useRef, useState, useEffect } from "react";
function Timer() {
const [count, setCount] = useState(0);
const intervalRef = useRef();
useEffect(() => {
intervalRef.current = setInterval(() => {
setCount((prev) => prev + 1);
}, 1000);
return () => clearInterval(intervalRef.current);
}, []);
return <h1>Time: {count}s</h1>;
}
为什么不用 useState 存储 interval ID?
因为 useState
每次更新都会触发组件重新渲染。而计时器 ID 本身并不影响 UI,适合用 useRef
安全存放,避免不必要的渲染。
useRef 跟踪先前的值
另一个高频技巧:在多次渲染之间记录某个 prop
或 state
的上一个值。
import React, { useEffect, useRef, useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
const prevCount = useRef();
useEffect(() => {
prevCount.current = count;
}, [count]);
return (
<div>
<p>Current: {count}</p>
<p>Previous: {prevCount.current}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
适用场景包括:
- 动画过渡中基于前后帧做插值
- 对比
prop/state
的变化以触发特定逻辑 - 撤销/重做等需要历史值的能力
useRef 与 useState — 何时使用?
特性 | useRef | useState |
---|---|---|
需要在渲染间隔期间保持持久 | ✅ | ✅ |
更改时触发重新渲染 | ❌ | ✅ |
存储 DOM 节点引用 | ✅ | ❌ |
跟踪值但不应影响 UI 渲染 | ✅ | ❌ |
经验法则
当你需要“记住一些东西”,但它不应驱动 UI 渲染或参与 diff 时,用 useRef
;当它需要影响 UI(显示/隐藏、文本、样式等)时,用 useState
。
表单验证中的 useRef
在不需要根据输入过程中的变化实时更新 UI 时,表单可用 useRef
直接读取值:
function LoginForm() {
const usernameRef = useRef();
const passwordRef = useRef();
const handleSubmit = (e) => {
e.preventDefault();
const username = usernameRef.current.value;
const password = passwordRef.current.value;
if (!username || !password) {
alert("Both fields are required!");
} else {
alert(`Welcome, ${username}!`);
}
};
return (
<form onSubmit={handleSubmit}>
<input ref={usernameRef} placeholder="Username" />
<input ref={passwordRef} type="password" placeholder="Password" />
<button type="submit">Login</button>
</form>
);
}
如果需要根据输入变化实时反馈校验或联动 UI,则应使用 useState
管理受控输入。
useRef 的注意事项
- 它不是用来替代任何“影响 UI 的状态”
- 更新
ref.current
不会通知 React,也不会触发渲染 - 不应用它来控制“显示/隐藏 UI”等需要驱动视图变化的场景
总结
useRef
让你可以:
- 直接访问 DOM 节点(如
.focus()
) - 在渲染间隔安全存储计时器或任意可变值
- 追踪先前值,构建撤销/动画等增强能力
- 编写更高性能、更稳定的 React 组件
它是 React 中的“低调英雄”。掌握它的边界与最佳实践,你会在恰当的地方用好它,并获得更流畅可靠的交互体验。
原文链接:https://code.ifrontend.net/archives/1375,转载请注明出处。
评论0