理解:组件内的标签可以定义ref属性来标识自己(可联想到标签内的id属性)
1、三种不同形式的ref
(1)字符串形式的ref(不推荐使用)
<input ref="input1" />
class Demo extends React.Component{
showData = () => {
console.log(this)
console.log(this.refs.input1)
console.log(this.refs.input1.value)
}
render() {
return (
<div>
<input ref="input1" type="text" placeholder="请输入内容" />
<button onClick={this.showData}>点击提示输入的内容</button>
</div>
)
}
}
当点击按钮后,调用showData
函数,由于该函数是箭头函数,所以this
指的就是该Demo组件的实例对象,console.log(this)
会使得控制台输出这个实例对象,该实例对象的身上会有很多内容,如props
、state
、context
、showData
函数等,当然也有refs
。
如果给input
标签指定一个ref
属性,如ref='input1'
,那么在该实例对象的身上,refs
里就会有{input1: input}
这个对象,它的key就是我们当初指定的input1
,value就是input
这个标签节点(真实DOM节点)。那么,我们就可以用this.refs.input1
来获取这个标签节点,而this.refs.input1.value
就能获取到我们输入的值。
字符串形式ref
存在一些效率上的问题,是过时的api,可能会在未来版本被移除。官网不推荐使用。
(2)回调形式的ref
<input ref={c => this.input1 = c}/>
class Demo extends React.Component{
showData = () => {
console.log(this)
console.log(this.input1)
console.log(this.input1.value)
}
render() {
return (
<div>
<input ref={currentNode => this.input1 = currentNode} type="text" placeholder="请输入内容" />
<button onClick={this.showData}>点击提示输入的内容</button>
</div>
)
}
}
在input
标签内加上ref={currentNode => this.input1 = currentNode}
后,在执行代码的时候,react会帮我们调用这个回调函数并执行,那么,在实例对象自身的refs
属性内就会有{input1: input}
这个对象了。
ref
属性内的回调函数做的事就是:把当前所处的input
节点(currentNode
)赋值给Demo实例自身,并起名为input1
,此处的this
就是实例对象自身。那我们在用的时候就只要this.input1
就能拿到这个input
节点了
ref
内回调函数的执行次数问题:如果ref
回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数null
,然后第二次会传入参数DOM元素。这是因为在每次渲染时会创建一个新的函数实例,所以React清空旧的ref
并且设置新的。通过将ref
的回调函数定义成class
的绑定函数的方式(如下代码块所示)可以避免上述问题,但是大多数情况下它是无关紧要的。
class Demo extends React.Component{
showData = () => {
console.log(this)
console.log(this.input1)
console.log(this.input1.value)
}
saveInput = () => {
this.input1 = c
console.log('saveInput()', c)
}
render() {
return (
<div>
<input ref={this.saveInput} type="text" placeholder="请输入内容" />
<button onClick={this.showData}>点击提示输入的内容</button>
</div>
)
}
}
(3)createRef创建ref容器
myRef=React.createRef()
<input ref={this.myRef}/>
class Demo extends React.Component{
myRef = React.createRef()
showData = () => {
console.log(this.myRef)
console.log(this.myRef.current)
console.log(this.myRef.current.value)
}
render() {
return (
<div>
<input ref={this.myRef} type="text" placeholder="请输入内容" />
<button onClick={this.showData}>点击提示输入的内容</button>
</div>
)
}
}
React.createRef()
调用后可以返回一个容器,该容器可以存储被ref
所标识的节点。我们把节点放在了myRef
这个变量中。当我们在input
节点内加上ref={this.myRef}
后,执行代码的时候就会把ref
所在的当前节点直接存储到了myRef
这个容器中。当我们在控制台输出this.myRef
时,显示的是{current: input}
,因此,我们可以用this.myRef.current
来拿到这个input
节点了。如果要获取用户在该节点输入的值,只要this.myRef.current.value
就可以了。
需要注意的是,一个容器只能存储一个节点,后放进去的就会替代前面放进去的内容。
此外,最近遇到一种使用createRef
创建ref
容器的用法:即,如何做到父组件调用子组件对象的方法。
class ProductAddUpdate extends Component {
constructor(props) {
super(props)
//创建用来保存ref标识的标签对象的容器
this.pw = React.createRef()
}
submit = () => {
//进行表单验证,如果都通过了,才发送请求
this.props.form.validateFields((err, values) => {
if (!err) {
//表单验证通过,可以发起ajax请求了
console.log("表单验证通过!", values);
const imgs = this.pw.current.getImgs()
console.log('imgs', imgs)
} else {
message.error("表单验证不通过!");
}
});
};
render(){
return (
<Card>
<Form>
<Form.Item>
<PicturesWall ref={this.pw} imgs={imgs}/>
</Form.Item>
<Form.Item>
<Button type="primary" onClick={this.submit}>
提交
</Button>
</Form.Item>
</Form>
</Card>
)
}
}
export default class PicturesWall extends Component {
getImgs = () => {
return this.state.fileList.map(file => file.name);
};
render(){
return (
......
)
}
}
父组件ProductAddUpdate
需要调用子组件<PictureWall />
内的方法getImgs()
:首先,一般在构造器内创建ref
容器: this.pw = React.createRef()
;其次,将ref
容器交给需要获取的标签元素: <PicturesWall ref={this.pw} />
,它会自动将标签对象(<PictureWall />
)添加为pw
容器对象的current
属性,注意,标签对象就是组件对象;最后,通过ref
容器读取标签对象: this.pw.current
。那么,我们就可以通过const imgs = this.pw.current.getImgs()
来获取到子组件内的图片名称数组了。