通俗点来讲ref的使用类似于我们写标签的时候的id,比如我们要实现如下的这个简单页面
左边的框的内容当点击按钮的时候会alert,而右边的框则是失去焦点的时候alert
我们一般情况下要通过document获取这个两个dom元素,然后alert,但是react已经为我们处理了这个问题,就是使用ref来代替使用document.getElementById()的方法,我们知道继承了React.Component的实例具有的属性如下
其中的refs的内容就是我们使用ref来标记一个dom元素,具体看如下的代码
class Refest extends React.Component {
showData = () => {
const {input1} = this.refs;
alert(input1.value)
}
showData2 = () => {
const {input2} = this.refs;
alert(input2.value)
}
render() {
return (
<div>
<input ref="input1" type="text" placeholder="点击显示" />
<button onClick={this.showData}>点击这个地方显示左边的</button>
<input ref="input2" onBlur={this.showData2} type="text" placeholder="这个不需要点击只需失去焦点"/>
</div>
);
}
}
ReactDOM.render(<Refest />,document.getElementById('app'))
注意一下啊,这里我们在两个input标签上分别使用了ref,相当于加了id来标记这个标签,随后我们就会在实例的this.refs中得到具有标记名的标签,顺带一提,这个ref标记的是真实dom上的标签。
以上记录的是字符串形式的ref,但是官网是不建议这样使用的,单纯的来讲原因就是这样写效率不是太高,那第二种写的方式是什么呢?叫做回调函数的形式。
什么是回调函数呢?具体的就是满足三点,1、定义的一个函数。2、自己没有调用。3、这个函数执行了。
那么如何用回调去使用ref呢?
class Refest extends React.Component {
showData = () => {
const { input1 } = this;
alert(input1.value)
}
showData2 = () => {
const { input2 } = this;
alert(input2.value)
}
render() {
return (
<div>
<input ref={currentNode => this.input1 = currentNode} type="text" placeholder="点击显示" />
<button onClick={this.showData}>点击这个地方显示左边的</button>
<input ref={currentNode => this.input2 = currentNode} onBlur={this.showData2} type="text" placeholder="这个不需要点击只需失去焦点" />
</div>
);
}
}
ReactDOM.render(<Refest />, document.getElementById('app'))
可以看到我们用一个回调函数的形式去把当前的这个标签节点赋值给了对象实例作为一个属性,于是乎我们在方法上就可以将其解构出来。(我们事先已经测验过了,这个函数在执行的时候是会被执行的!!!)
这里有一个小的细节就是关于这个回调形式的ref,我们现在的函数是采用内联的形式直接写在标签内的,这样会出现一个小问题就是当页面的节点更新的时候这个回调会被执行两次,第一次执行的时候回调函数传过来的参数是null,第二次执行的时候才会是当前节点,当前这样不会产生什么太大的问题。
如果使用类中定义的函数就不会有这个问题,当然这种问题已经被忽略,一定要较真的说,那就是这个问题。
最后一种使用ref的方式就是使用官方提供的方法——React.createRef(),这样可以创建一个类似于容器的东西来存储ref,具体参考以下的代码。
class Refest extends React.Component {
myRef1 = React.createRef();
myRef2 = React.createRef();
showData = () => {
alert(this.myRef1.current.value)
}
showData2 = () => {
alert(this.myRef2.current.value)
}
render() {
return (
<div>
<input ref={this.myRef1} type="text" placeholder="点击显示" />
<button onClick={this.showData}>点击显示左边的内容</button>
<input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="失焦时显示" />
</div>
);
}
}
ReactDOM.render(<Refest />, document.getElementById('app'))
我们使用官方提供的方法建立了两个ref的容器,然后分别将两个节点存入到对应的ref容器中,这里就注意一个ref容器只能存储一个节点标签,如果存了多个就会后面的覆盖掉前面的那个,最后我们在取值的时候要使用current来取值,可以理解这个current类似于key,其中存的东西就是标签。
同时,react会把给标签加的这些click方法采用事件委托的方式放到最外层从而提升效率。
官方也建议我们少用ref,并且在一些情况下这些ref是可以省略的,比如上述的失焦时显示就可以省略ref。
然后input标签中的ref删掉就ok了,采用的是事件源的方式。