React Refs
大多数场景下数据自上而下流动、界面由 state 驱动。但偶尔需要「命令式」地操作一下 DOM——聚焦输入框、测量元素宽高、控制视频播放,或者保存定时器 id 这类与渲染无关的值。Refs 就是 React 为这类需求留出的后门,本节讲清它的用法与边界。
为什么用 Ref 而不是 state
state 与 ref 都能「记住」数据,关键区别在于:state 一变就重新渲染组件,而修改 ref 不会触发渲染。像 DOM 节点、定时器句柄这类数据放进 state,只会带来无谓的渲染。
| 对比 | state | ref |
|---|---|---|
| 更新方式 | setState 函数 | 直接改 ref.current |
| 是否触发渲染 | 是 | 否 |
| 典型用途 | 需要展示的数据 | DOM 节点、句柄等 |
useRef 与 ref 属性
函数组件中用 useRef(null) 创建 ref,把它传给元素的 ref 属性,之后 ref.current 就指向真实的 DOM 节点。
import { useRef } from 'react';
export default function FocusDemo() {
const inputRef = useRef(null);
function handleFocus() {
inputRef.current.focus(); // 直接调用 DOM 方法
}
return (
<>
<input ref={inputRef} placeholder="点按钮聚焦这里" />
<button onClick={handleFocus}>聚焦输入框</button>
</>
);
}
不受控组件:用 ref 读值
不设 value、不监听 onChange,输入框内容交给浏览器维护,需要时再用 ref 读取,这就是「不受控组件」,适合简单、无需校验的表单。
function Uncontrolled() {
const nameRef = useRef(null);
function submit() {
console.log('输入的值:', nameRef.current.value);
// 输出:输入的值:张三
}
return (
<>
<input ref={nameRef} defaultValue="张三" />
<button onClick={submit}>读取输入</button>
</>
);
}
ref 保存跨渲染的值
计数器用 ref 保存时,累加不会触发界面刷新,适合记录「只需要记、不需要显示」的数据。
function ClickLog() {
const times = useRef(0);
function click() {
times.current += 1; // 只改内存,不触发渲染
console.log('共点击', times.current, '次');
// 输出:共点击 1 次(每点一次数字加一)
}
return <button onClick={click}>记录点击</button>;
}
补充:React 16.3 之前的类组件用 React.createRef(),函数组件统一用 useRef,思想相同。ref 内容变化不会通知视图,所以「要展示的内容」不要放进 ref。
小结
Refs 提供一条绕开 state 的通道:用 ref 属性拿到 DOM 节点做聚焦、测量等操作,或用 useRef 保存不触发渲染的临时数据。判断标准很简单——界面需要跟着变就用 state,不需要就考虑 ref。