useRef深层剖析
useRef
ref 使用场景
通常,当你的组件需要“跳出” React 并与外部 API 通信时,你会用到 ref —— 通常是不会影响组件外观的浏览器 API。以下是这些罕见情况中的几个:
- 存储 timeout ID
- 存储和操作 DOM 元素,涉及非受控组件。赋值给标签,目的是获取 DOM 元素;赋值给类组件,目的是获取组件的实例;
- 存储不需要被用来计算 JSX 的其他对象。
如果你的组件需要存储一些值,但不影响渲染逻辑,请选择 ref。
类组件的三种创建和使用方式
方式一——字符串形式的 ref(过时了,未来版本可能移除,开发时不推荐使用,效率低)
通过 this.refs
访问
1 |
|
方式二——回调形式的 ref
1 |
|
1 |
|
不能通过 this.refs
访问,因为这些 ref 都成了实例属性
React 不会帮你执行未知属性的回调函数
1 |
|
如果 ref 回调函数是以**内联函数的形式定义的,在更新过程中它会被执行两次,第一次传入参数 null,第二次传入参数 DOM 元素。这是因为在每次渲染时创建一个新的实例,所以 React 清空旧的 ref 并设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无效的(无关紧要)**
方式三——createRef 创建 ref 容器(最推荐)
React.createRef 调用后可以返回一个容器,该容器可以存储被 ref 所标识的节点,该容器是“专人专用”的,多个 ref 的值可以相互独立
ref 是实例的属性,不能通过 this.refs 访问
this.myRef.current 是当前引用的 DOM,this.myRef.current.value 是当前 DOM 的值
1 |
|
hooks 组件通过 useRef 创建 ref 对象
在函数组件中,可以基于 useRef
获取 DOM 元素!类似于类组件中的 :
- ref={x=>thix.box=x}
- React.createRef
函数组件中创建 ref 对象的两种方法:
let box1 = useRef(null)
let box2 = React.createRef();
注意:
React.createRef 也是 ref 对象,在类组件和函数组件中都可以使用
useRef 只能在函数组件中使用,所有的 hooks 函数都只能在函数组件中使用,在类组件中使用会报错
ref 只能在 DOM 创建之后才能获取 DOM 元素,也就是说在 useLayoutEffect 阶段就可以使用
createRef 性能比 useRef 差——每次渲染创建新 ref 对象
- createRef 每次渲染都会返回一个新的引用
- 而 useRef 每次都会返回相同的引用
1 |
|
总结:在类组件中,创建 Ref 对象,基于 React.createRef 处理;在函数组件中为了保证性能使用 useRef
ref 的 DOM 用法总结【useRef】
- 给元素标签设置 ref,目的:获取对应的 DOM 元素
- 给类组件设置 ref,目的:获取当前调用组件创建的实例(后续可以根据实例获取子组件中的相关信息)
1
2
3
4
5
6
7
8
9// 基于ref获取子组件的实例,这样基于实例,可以调用子组件内部,挂载到实例上的东西
class Child extends React.Component {
state = { x: 1000 };
render() {
return <div className="child-box">
{this.state.x}
</div>;
}
} - 给函数组件/hooks 设置 ref,直接报错:Function components cannot be given refs. Attempts to access this ref wil fail,但是可以配合 React.forwardRef 实现 ref 的转发。目的:获取函数子组件内的某个 DOM 元素
1
2
3
4
5
6
7// 基于forwardRef实现ref转发,目的:获取子组件内部的某个元素
const Child = React.forwardRef(function Child(props, ref) {
// console.log(ref); //在DEMO中,调用Child的时候,传递的ref对象「x」
return <div className="child-box">
<span ref={ref}>哈哈哈</span>
</div>;
});