React 19 入门教程
useRef 操作 DOM
本教程共 50 篇 · 第 31 篇 · 更新于 2026-07-29 · 约 6 分钟阅读
ReactuseRefDOM操作ref回调forwardRef
31. useRef 操作 DOM
本节目标:掌握用 ref 访问 DOM 节点,实现聚焦、滚动、测量等操作,了解 ref 回调和 forwardRef。
获取 DOM 节点的 ref
React 自动管理 DOM,但在需要时你可以”插手”。三个步骤:
import { useRef } from 'react';
function Form() {
// 1. 创建 ref
const inputRef = useRef(null);
function handleClick() {
// 3. 通过 ref.current 访问 DOM
inputRef.current.focus();
}
return (
<>
{/* 2. 绑定到 JSX */}
<input ref={inputRef} />
<button onClick={handleClick}>聚焦输入框</button>
</>
);
}
React 挂载组件后,会把 DOM 节点赋值给 ref.current。卸载时重置为 null。
ref.current 初始值是 null
第一次渲染时
ref.current是null,因为 DOM 还没创建。在 effect 或事件处理函数里才能安全访问。
聚焦和选择文本
function SearchBox() {
const inputRef = useRef(null);
function handleFocus() {
inputRef.current.focus();
}
function handleSelectAll() {
inputRef.current.select(); // 选中全部文本
}
return (
<>
<input ref={inputRef} defaultValue="搜索内容" />
<button onClick={handleFocus}>聚焦</button>
<button onClick={handleSelectAll}>全选</button>
</>
);
}
滚动到指定元素
function ScrollDemo() {
const targetRef = useRef(null);
function handleScroll() {
targetRef.current.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}
return (
<>
<button onClick={handleScroll}>滚到底部</button>
<div style={{ height: '1000px' }}>占位</div>
<div ref={targetRef}>目标位置</div>
</>
);
}
测量 DOM 尺寸
function MeasureDemo() {
const boxRef = useRef(null);
const [size, setSize] = useState({ width: 0, height: 0 });
useEffect(() => {
const { width, height } = boxRef.current.getBoundingClientRect();
setSize({ width, height });
}, []);
return (
<>
<div ref={boxRef} style={{ width: '50%', background: '#eee' }}>
我是被测量的盒子
</div>
<p>尺寸:{size.width.toFixed(0)} x {size.height.toFixed(0)}</p>
</>
);
}
ref 回调
ref 可以是一个函数,React 会在挂载和卸载时调用它:
function MeasureList() {
const [heights, setHeights] = useState([]);
function measureRef(node) {
if (node) {
// 挂载时
console.log('新节点高度:', node.getBoundingClientRect().height);
} else {
// 卸载时
console.log('节点被移除');
}
}
return (
<ul>
{items.map(item => (
<li key={item.id} ref={measureRef}>
{item.text}
</li>
))}
</ul>
);
}
ref 回调适合列表场景
列表数量不固定时,ref 回调比
useRef数组更方便。
访问子组件的 DOM:forwardRef
默认情况下,ref 只能指向 DOM 元素,不能指向组件。forwardRef 让组件可以转发 ref 到内部的 DOM:
import { forwardRef, useRef } from 'react';
const FancyInput = forwardRef(function FancyInput(props, ref) {
return <input ref={ref} {...props} className="fancy" />;
});
function Parent() {
const ref = useRef(null);
function handleFocus() {
ref.current.focus(); // 直接聚焦子组件的 input
}
return (
<>
<FancyInput ref={ref} />
<button onClick={handleFocus}>聚焦</button>
</>
);
}
React 19 中 ref 可以作为普通 prop
React 19 开始,函数组件可以直接接收 ref 作为 prop,不需要 forwardRef。但 forwardRef 仍然兼容。
多个 ref
一个组件可以有多个 ref:
function MultiRefForm() {
const nameRef = useRef(null);
const emailRef = useRef(null);
const submitRef = useRef(null);
function handleStart() {
nameRef.current.focus();
}
return (
<>
<input ref={nameRef} placeholder="姓名" />
<input ref={emailRef} placeholder="邮箱" />
<button ref={submitRef} onClick={handleStart}>提交</button>
</>
);
}
操作 DOM 的安全原则
- 只做 React 不擅长的事:聚焦、滚动、测量。其他交给 React。
- 不要修改 React 管理的 DOM:React 可能覆盖你的修改。
- 在 effect 里操作:确保 DOM 已经存在。
- 清理副作用:在 effect 返回的清理函数里撤销。
不要绕过 React 更新 DOM
用 ref 直接修改
innerHTML、textContent等,React 不知道这些变化,下次渲染会覆盖。
本节要点
ref属性绑定 DOM 节点到useRefref.current.focus()让元素获得焦点scrollIntoView()平滑滚动getBoundingClientRect()测量尺寸forwardRef让子组件暴露内部 DOM 给父组件