文章目录
useRef
ref使用场景
通常,当你的组件需要“跳出” React 并与外部 API 通信时,你会用到 ref —— 通常是不会影响组件外观的浏览器 API。以下是这些罕见情况中的几个:
-
存储
timeout ID
-
存储和操作
DOM 元素
,涉及非受控组件。赋值给标签,目的是获取DOM元素;赋值给类组件,目的是获取组件的实例;
-
存储不需要被用来计算 JSX 的其他对象。
如果你的组件需要存储一些值,但不影响渲染逻辑,请选择 ref。
类组件的三种创建和使用方式
方式一——字符串形式的ref(
过时了,未来版本可能移除,开发时不推荐使用,效率低
)
通过
this.refs
访问
<input ref="input1"/>
方式二——回调形式的ref
React帮我们调用回调函数,并且将当前的DOM节点传入回调函数
<input ref={(currentNode) => {this.input1 = currentNode}} />
// currentNode是当前所处的节点,也就是这个input
不能通过
this.refs
访问,因为这些ref都成了实例属性
React不会帮你执行未知属性的回调函数
<input ref={c => this.input1 = c } ahh={() => {console.log(1)}} type="text" placeholder="点击按钮提示数据"/>
如果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的值
myRef = React.createRef();
<input ref={this.myRef} />
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 每次都会返回相同的引用
import React, { useState, useEffect, useRef } from "react";
import { Button } from 'antd';
import './Demo.less';
let prev1,
prev2;
const Demo = function Demo() {
let [num, setNum] = useState(0);
// 函数组件中创建ref对象的两种方法
let box1 = useRef(null),
box2 = React.createRef();
if (!prev1) {
// 第一次DEMO执行,把第一次创建的REF对象赋值给变量
prev1 = box1;
prev2 = box2;
} else {
// 第二次DEMO执行,我们验证一下,新创建的REF对象,和之前第一次创建的REF对象,是否一致?
console.log(prev1 === box1); //true useRef再每一次组件更新的时候(函数重新执行),再次执行useRef方法的时候,不会创建新的REF对象了,获取到的还是第一次创建的那个REF对象!!
console.log(prev2 === box2); //false createRef在每一次组件更新的时候,都会创建一个全新的REF对象出来,比较浪费性能!!
// 总结:在类组件中,创建REF对象,我们基于 React.createRef 处理;但是在函数组件中,为了保证性能,我们应该使用专属的 useRef 处理!!
}
useEffect(() => {
console.log(box1.current);
console.log(box2.current);
});
return <div className="demo">
<span className="num" ref={box1}>{num}</span>
<span className="num" ref={box2}>哈哈哈</span>
<Button type="primary" size="small"
onClick={() => {
setNum(num + 1);
}}>
新增
</Button>
</div>;
};
总结:在类组件中,创建Ref对象,基于React.createRef处理;在函数组件中为了保证性能使用useRef
ref的DOM用法总结【useRef】
-
给元素标签设置ref,目的:获取对应的DOM元素
-
给类组件设置ref,目的:获取当前调用组件创建的实例(后续可以根据实例获取子组件中的相关信息)
// 基于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元素
// 基于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>; });