react ref(建议收藏)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观
在 React 开发中,开发者常常会遇到需要直接操作 DOM 元素或访问组件实例的场景。此时,React Ref 就像一把“遥控器”,允许我们打破组件的封装性,直接与底层元素或对象进行交互。无论是聚焦输入框、控制动画,还是集成第三方库,Ref 都是不可或缺的工具。本文将从基础概念到实战案例,逐步解析如何正确使用 Ref,并避免常见陷阱。
一、Ref 是什么?为什么需要它?
1.1 Ref 的核心概念
Ref(Reference)是 React 提供的一种特殊属性,用于直接访问 DOM 元素或 React 组件实例。它打破了 React 的单向数据流和虚拟 DOM 的限制,允许开发者绕过状态(State)和属性(Props)的更新机制,实现对元素的直接操作。
比喻:
想象你有一台遥控器(Ref),可以随时控制电视(DOM 元素)的开关,而无需等待电视内部复杂的电路(React 的渲染流程)完成状态更新。
1.2 Ref 与 State 的区别
- State:用于管理组件内部的数据状态,遵循 React 的响应式更新机制。
- Ref:提供对 DOM 或实例的直接引用,不触发渲染。
例子:
聚焦输入框时,通过 Ref 直接调用 focus()
方法比用 State 控制 autofocus
属性更直接高效。
二、Ref 的基础用法
2.1 在类组件中使用 Ref
在类组件中,通过 React.createRef()
创建 Ref,并将其赋值给组件的属性:
class InputFocus extends React.Component {
constructor(props) {
super(props);
// 创建 Ref
this.inputRef = React.createRef();
}
componentDidMount() {
// 通过 current 属性访问真实 DOM
this.inputRef.current.focus();
}
render() {
return <input ref={this.inputRef} />;
}
}
关键点:
createRef()
返回一个空对象,通过.current
属性获取目标元素。- Ref 通常在
componentDidMount
或事件中使用,避免在渲染期间操作 DOM。
2.2 在函数组件中使用 useRef
React 16.8 引入的 useRef
钩子,让函数组件也能轻松使用 Ref:
import { useRef, useEffect } from 'react';
function InputFocus() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []); // 空依赖数组确保仅执行一次
return <input ref={inputRef} />;
}
对比:
| 类型 | 方法 | 语法示例 |
|---------------|-----------------------|-----------------------------------|
| 类组件 | React.createRef() | this.inputRef = React.createRef();
|
| 函数组件 | useRef() | const inputRef = useRef(initialValue);
|
三、Ref 的典型应用场景
3.1 自动聚焦输入框
通过 Ref 直接调用 focus()
方法,无需引入额外的 State:
function AutoFocusInput() {
const inputRef = useRef();
useEffect(() => {
inputRef.current.focus();
}, []);
return <input ref={inputRef} />;
}
3.2 手动控制动画
在 React 的虚拟 DOM 中,某些动画需要直接操作元素的样式或属性。例如,使用 Ref 实现点击后放大效果:
function ScaleButton() {
const buttonRef = useRef();
const handleClick = () => {
// 直接修改 style 属性
buttonRef.current.style.transform = 'scale(1.2)';
};
return (
<button ref={buttonRef} onClick={handleClick}>
点击放大
</button>
);
}
3.3 集成第三方库
当使用非 React 的库(如 D3.js 或第三方图表库)时,Ref 可以获取 DOM 元素供其操作:
function ChartContainer() {
const chartRef = useRef();
useEffect(() => {
// 假设 thirdPartyLib 需要 DOM 元素
thirdPartyLib.init(chartRef.current);
}, []);
return <div ref={chartRef} style={{ width: '400px' }} />;
}
四、进阶技巧与注意事项
4.1 Ref 的生命周期与 current 属性
- Ref 的
current
属性在组件挂载时被赋值,在卸载时设为null
。 - 避免在渲染函数中直接依赖 current,因为这可能导致无限循环(如
ref.current.value
与 State 耦合)。
4.2 跨组件传递 Ref
通过 Props 将 Ref 传递给子组件:
// 父组件
function Parent() {
const childRef = useRef();
return (
<Child ref={childRef} />
);
}
// 子组件
function Child(props, ref) {
// 使用 forwardRef 包装
return <div ref={ref}>我是子组件</div>;
}
// 正确写法
const Child = forwardRef((props, ref) => {
return <div ref={ref}>我是子组件</div>;
});
4.3 避免滥用 Ref 的陷阱
- 不要用 Ref 替代 State:例如,输入框的值应通过 State 管理,而非直接读取
ref.current.value
。 - 慎用 Ref 调试:过度依赖 Ref 可能掩盖组件设计问题,影响可维护性。
五、Ref 的高级用法
5.1 Ref 回调函数
通过 ref
属性直接传递回调函数,React 会自动将元素赋值给该函数:
function InputFocus() {
const handleRef = (node) => {
if (node) {
node.focus();
}
};
return <input ref={handleRef} />;
}
5.2 自定义 Hook 结合 Ref
将 Ref 的逻辑封装为自定义 Hook,提升代码复用性:
// 自定义 Hook
function useAutoFocus() {
const ref = useRef();
useEffect(() => {
ref.current.focus();
}, []);
return ref;
}
// 使用 Hook
function AutoFocusInput() {
const inputRef = useAutoFocus();
return <input ref={inputRef} />;
}
六、总结:合理使用 Ref 的核心原则
- 明确场景:仅在 State 或 Props 无法满足需求时使用 Ref。
- 保持简洁:避免在渲染函数中直接依赖 Ref 的值。
- 谨慎传递:通过 Props 或自定义 Hook 管理 Ref 的复杂性。
通过本文的讲解,开发者可以掌握 React Ref 的核心概念、常见用法及进阶技巧。无论是处理表单、动画,还是集成第三方库,Ref 都能成为你高效开发的得力工具。记住,合理使用 Ref 能提升代码的灵活性,但过度滥用则可能引入不必要的复杂性。
希望本文能帮助你在 React 开发中更自信地驾驭 Ref 的强大能力!