个人公众号
个人公众号,求关注公众号~ 求指导,求点赞,求评论。
写在前面的废话
什么时候使用ref的环境就不说了,比如我们要获取一个输入框的value,无法通过state去获取,这时候用ref就很合适。
本文的重点介绍的是ref的四种方法,因为现在使用的react版本是18.2,所以部分的方法都算是已经过时了,并有自己的局限性,所以如果方便,更推荐使用useRef的方式。
stringRef
在描述stringRef之前,先贴上一段官网中关于stringRef的描述。
如果你以前使用过 React,你可能熟悉一个较旧的 API,其中属性是一个字符串,比如 ,而 DOM 节点被访问为 .我们建议不要这样做,因为字符串引用存在一些问题,被认为是遗留的,并且可能会在将来的某个版本中删除。 在知道了字符串的ref问题之后,由于部分旧的代码我们还会使用到stringRef,我们还是得看看stringRef的使用。
javascript
import { PureComponent, ReactNode } from "react";
class RefComponent extends PureComponent {
handleClick = () => {
const element: any = this.refs.stringRef;
console.log(element?.value);
};
render(): ReactNode {
return (
<div>
<input ref="stringRef" />
<button
onClick={() => {
this.handleClick();
}}
>
点击获取输入框的值
</button>
</div>
);
}
}
export default RefComponent;
效果如下:
当点击了我们的按钮的时候,就直接获取到了输入框中的值。
而在控制台中也有这么一个警告
Warning: A string ref, "stringRef", has been found within a strict mode tree. String refs are a source of potential bugs and should be avoided. We recommend using useRef() or createRef() instead.
Learn more about using refs safely here: reactjs.org/link/strict...
而这个警告也与官网的提示一样,讲述了stringRef是一个"deprecated"的API,建议我们使用useRef或者是createRef这两个API。 除此之外,在第五行的代码中,由于我们使用了this,而只有在类组件中,才存在了this,因此我们无法在函数组件中使用到stringRef。 综上所述,不建议再使用stringRef,除非你的项目很老,只能使用stringRef。
createRef
相对于stringRef,createRef算是比较正常点的,而因为stringRef有bug的原因,所以react也推荐在class组件使用createRef。
与直接使用stringRef不同,我们需要先在constructor中,使用createRef构建一个ref对象,再将其绑定到元素上去,这样子才可以去获取到元素。代码如下:
javascript
import React, { PureComponent, ReactNode, RefObject } from "react";
class RefComponent extends PureComponent {
inputRef: RefObject<HTMLInputElement> | undefined;
constructor(props: {} | Readonly<{}>) {
super(props);
this.inputRef = React.createRef();
}
handleClick = () => {
const element: any = this.inputRef;
console.log(element?.current?.value);
};
render(): ReactNode {
return (
<div>
<input ref={this.inputRef} />
<button
onClick={() => {
this.handleClick();
}}
>
点击获取输入框的值
</button>
</div>
);
}
}
export default RefComponent;
需要注意的是,我们无法再直接使用ref.value的方式去获取到值,而是需要使用ref?.current?.value的方式获取到值。
而将createRef放置在constructor中的原因,是因为如果放在
componentDidMount的时候,很容易出现createRef未能完成初始化,会出现undefined,同时,也最好使用?.的形式,防止放在constructor的时候,也未能及时初始化的情况。 同样的,本代码也只能放在类组件中使用,无法放在函数组件中使用,因为没有this.... 也需要注意一点,这儿我用的类型是RefObject,而部分同学喜欢用LegacyRef,后者泛指的是Ref类型,是兼容之前stringRef的版本。 效果如下:
CallbackRef
ref属性,除了可以接受ref对象之外,也可以接受函数,这就称之为CallbackRef,也没啥好讲的,代码如下
javascript
import React, { LegacyRef, PureComponent, ReactNode, RefObject } from "react";
class RefComponent extends PureComponent {
inputRef: HTMLElement | undefined | null;
handleClick = () => {
const element: any = this.inputRef;
console.log(element?.value);
};
render(): ReactNode {
return (
<div>
<input
ref={(ref) => {
this.inputRef = ref;
}}
/>
<button
onClick={() => {
this.handleClick();
}}
>
点击获取输入框的值
</button>
</div>
);
}
}
export default RefComponent;
需要注意的是,这次的inputRef并不是RefObject,而是一个html对象,所以我们只需要当他做一个html对象处理就好了。
唯一要注意的是,依旧需要在类组件中使用,无法在函数组件中使用,毕竟都是对ref的操作。
useRef
与前面的类组件中对ref对象的操作,函数组件在获得了hook之后,也出现了对应的useRef钩子与之对应。毕竟ref对象都是只能在类组件中使用,前三个方式,无论怎么说,本质都是在类组件中对ref属性做一些变种,而useRef在函数式编程为王道的今天,更加的需要了解。 useRef返回的是一个可变的ref对象,类型为refObject,所以依旧需要用到上面的current属性去读取我们要的值。 useRef需要我们先使用userRef,接着在ref属性上使用回调函数的方式绑定ref。代码如下:
javascript
import { useRef } from "react";
function RefComponent() {
const inputRef = useRef<HTMLElement | null>();
const handleClick = () => {
const element: any = inputRef;
console.log(element?.current?.value);
};
return (
<>
<input
ref={(ref) => {
inputRef.current = ref;
}}
/>
<button
onClick={() => {
handleClick();
}}
>
点击获取输入框的值
</button>
</>
);
}
export default RefComponent;
效果如下:
最后的废话
大人,现在已经是2023年的年尾了,别想那么多了,用useRef吧...不要搞那么多事情了。