React Refs(保姆级教程)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新开坑项目:《Spring AI 项目实战》 正在持续爆肝中,基于 Spring AI + Spring Boot 3.x + JDK 21..., 点击查看 ;
- 《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观
什么是 React Refs?
在 React 开发中,我们常常会遇到这样的场景:需要直接操作某个 DOM 元素,或者在组件间传递复杂的数据结构。这时候,一个名为 React Refs 的功能就显得尤为重要。它可以看作是 React 组件与底层 DOM 之间的“钥匙”,帮助开发者突破虚拟 DOM 的限制,实现对特定元素的直接控制。
Refs 的核心作用
- 直接访问 DOM 元素:当需要聚焦输入框、测量元素尺寸或操作第三方库时,Refs 提供了安全的访问通道。
- 管理复杂状态:在无法通过 Props 或 State 传递时,Refs 可以存储并更新组件的内部状态。
- 协调组件行为:例如在表单验证时,通过 Refs 获取输入值进行校验。
Refs 的比喻理解
想象你有一座由乐高积木搭建的城堡(React 组件树),每个积木块(组件)都被精心排列。但当你需要移动某块特定的积木时,却无法通过常规的拼接方式直接触碰它。这时 Refs 就像是一把“万能钥匙”,允许你绕过常规的建造规则,直接定位并操作目标积木。
Refs 的基本用法
类组件中的 Refs
在基于 class 的组件中,可以通过 createRef
方法创建 Ref。以下是一个简单的例子:
class FocusInput extends React.Component {
constructor() {
super();
this.inputRef = React.createRef();
}
componentDidMount() {
// 自动聚焦输入框
this.inputRef.current.focus();
}
render() {
return <input ref={this.inputRef} />;
}
}
关键点解析:
React.createRef()
创建了一个可复用的 Ref 对象。this.inputRef.current
是 Ref 的真实目标,即 DOM 元素或组件实例。- 在
componentDidMount
生命周期中,确保 DOM 已渲染完成再调用方法。
函数组件中的 Refs
随着 Hooks 的引入,函数组件可以通过 useRef
钩子实现相同功能:
function FocusInput() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []);
return <input ref={inputRef} />;
}
对比分析:
| 特性 | 类组件 Refs | 函数组件 useRef |
|---------------------|--------------------------|-------------------------|
| 创建方式 | React.createRef()
| useRef(initialValue)
|
| 数据访问 | this.ref.current
| ref.current
|
| 依赖管理 | 需要维护实例属性 | 无需额外状态管理 |
| 适用场景 | 旧版代码维护 | 新项目首选方案 |
Refs 的常见使用场景
场景一:表单自动聚焦
在用户注册页面,密码输入框需要在页面加载后自动获得焦点。通过 Refs 可以轻松实现:
function LoginForm() {
const passwordRef = useRef();
useEffect(() => {
passwordRef.current.focus();
}, []);
return (
<div>
<input type="text" placeholder="用户名" />
<input
type="password"
ref={passwordRef}
placeholder="密码"
/>
</div>
);
}
效果对比: | 传统方法 | Refs 实现 | |-----------------------|-------------------------| | 需要手动查询 DOM 节点 | 通过 Ref 直接引用元素 | | 可能引发性能问题 | 受 React 生命周期保护 | | 难以维护 | 代码结构清晰易读 |
场景二:手动控制媒体播放
当使用 HTML5 视频元素时,需要通过 Refs 调用播放方法:
function VideoPlayer() {
const videoRef = useRef();
const handlePlay = () => {
videoRef.current.play();
};
return (
<div>
<video ref={videoRef} src="example.mp4" />
<button onClick={handlePlay}>播放</button>
</div>
);
}
场景三:第三方库集成
在使用 D3.js 或 Chart.js 等库时,需要通过 Refs 获取画布元素:
function ChartComponent() {
const chartRef = useRef();
useEffect(() => {
const chart = new Chart(chartRef.current, {
type: 'line',
data: {/* 数据配置 */},
});
return () => chart.destroy();
}, []);
return <canvas ref={chartRef} />;
}
Refs 的高级技巧
1. Refs 的合并使用
当需要将 Ref 传递给多个目标时,可以使用 useImperativeHandle
或 forwardRef
:
// 子组件
const ChildComponent = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
customMethod() {
// 自定义暴露的方法
},
}));
return <div>子组件内容</div>;
});
// 父组件
function ParentComponent() {
const childRef = useRef();
return (
<div>
<ChildComponent ref={childRef} />
<button onClick={() => childRef.current.customMethod()}>
触发子组件方法
</button>
</div>
);
}
2. Refs 的动态管理
在列表渲染时,可以通过数组形式管理多个 Ref:
function DynamicRefsList() {
const itemRefs = useRef([]);
const focusItem = (index) => {
itemRefs.current[index].focus();
};
return (
<div>
{[0, 1, 2].map((_, index) => (
<input
key={index}
ref={(el) => (itemRefs.current[index] = el)}
/>
))}
<button onClick={() => focusItem(1)}>聚焦第二个输入框</button>
</div>
);
}
3. Refs 的清理与生命周期
在组件卸载时,需要确保 Ref 的正确清理:
useEffect(() => {
const timer = setInterval(() => {
// 使用 ref.current 的逻辑
}, 1000);
return () => {
clearInterval(timer);
// 可选:将 ref.current 置为 null
};
}, []);
常见问题与最佳实践
问题一:Ref.current 为 null 的情况
当 Ref 被绑定到未渲染的元素时,current
属性会返回 null。解决方法包括:
- 在
useEffect
中确保 DOM 已加载 - 添加条件判断
if (ref.current) { ... }
问题二:过度使用 Refs
虽然 Refs 功能强大,但应避免以下情况:
- 替代 Props/State 传递简单数据
- 在渲染过程中直接修改 DOM 样式(优先使用 State)
- 创建大量 Ref 变量导致代码混乱
最佳实践总结
- 最小化使用:仅在必要时(如手动聚焦、第三方库集成)使用 Refs。
- 避免副作用:不要在渲染函数中直接操作 Ref.current。
- 类型标注:使用 TypeScript 时明确 Ref 的类型:
const inputRef = useRef<HTMLInputElement>(null);
结论
React Refs 是开发者突破虚拟 DOM 限制的重要工具,它像一把精准的手术刀,帮助我们在需要直接操作 DOM 或组件实例时游刃有余。通过本文的学习,读者应能掌握 Refs 的基本用法、核心场景及高级技巧。记住:合理使用 Refs 能提升开发效率,但过度依赖可能破坏 React 的数据流设计原则。建议在日常开发中优先使用 Props 和 State,仅在必要时通过 Refs 实现“最后1%”的功能需求。随着实践的深入,开发者将逐渐掌握 Refs 与 React 核心机制的平衡之道,最终构建出更灵活、高效的 React 应用。