什么是ref
组件内的标签可以定义ref属性来标识⾃⼰,然后通过ref标识来获取dom元素,进而操作dom元素(react中不提倡过多操作dom)。
ref的三种创建方式
1.字符串形式的ref
// 1. 创建类式组件
class Demo extends React.Component {
render() {
return (
<div>
{/* 字符串形式的ref */}
<input ref="input1" type="text" placeholder="请输入文字"/>
<button onClick={this.showData}>点击显示左侧数据</button>
</div>
)
}
// 显示左侧输入的数据
showData = () => {
let {input1} = this.refs
alert(input1.value)
}
}
// 2. 渲染组件到页面
ReactDOM.render(<Demo />, document.getElementById('test'))
2.回调形式的ref
字符串形式的ref由于存在效率上的问题, react官⽅不在推荐使⽤, 甚⾄可能在后续版本移除。
react官⽅推荐使⽤回调形式的的ref或者createRef
// 1. 创建类式组件
class Demo extends React.Component {
render() {
return (
<div>
{/* 回调形式的ref */}
<input ref={c => this.input1 = c} type="text" placeholder="请输入文字"/>
<button onClick={this.showData}>点击显示左侧数据</button>
</div>
)
}
// 显示左侧输入的数据
showData = () => {
let {input1} = this.refs
alert(input1.value)
}
}
// 2. 渲染组件到页面
ReactDOM.render(<Demo />, document.getElementById('test'))
什么是回调函数?
- 你定义的函数
- 你没有调⽤
- 函数最终执⾏了(别⼈调了)
3.createRef的使⽤
React.createRef调⽤之后返回⼀个容器, 该容器可以存储被ref标识的节点 该容器是专⼈专⽤, 每⼀个input都要使⽤单独的⼀个 虽然写法⽐较繁琐, 却是React官⽅最推荐的写法
class Demo extends React.Component{
// React.createRef 是一个容器,这个容器可以存储一个被ref标记的dom节点。
// 每一个由React.createRef创建的容器仅被使用一次,不允许重复使用。
input1 = React.createRef()
showData = () => {
const {current} = this.input1
console.log(this);
alert(current.value)
}
inputBlur = () => {
const {current} = this.input1
console.log(this);
alert(current.value)
}
render() {
return (
<div>
<input ref={this.input1} type="text" placeholder="点击按钮显示数据"/>
<button onClick={this.showData}>点击显示左侧数据</button>
{/**此时在这里让ref使用this.input1,是不对的,因为已经在上个input中使用过了**/}
<input ref={this.input1} type="text" onBlur={this.inputBlur} placeholder="焦点离开显示数据"/>
</div>
);
}
}
ReactDOM.render(<Demo/>, document.getElementById('test'))
如果出现重复使用的ref,那么此ref标记会标记最后一个使用该ref的dom元素,之前标记的dom元素获取不到。
回调ref中调⽤次数的问题
class Demo extends React.Component{
state = {isHot: false}
showData = () => {
const {input1} = this
alert(input1.value)
}
changeWeather = () => {
this.setState({isHot: !this.state.isHot})
}
refFunction = (param) => {
console.log('bb', param);
}
render() {
const {isHot} = this.state
return (
<div>
<h1 onClick={this.changeWeather}>{isHot ? '炎热': '凉爽'}</h1>
{/**内联形式的ref回调, ref回调函数会执行两次*/}
<input ref={(c) => {this.input1 = c; console.log('aa', c);}} type="text" placeholder="点击按钮显示数据"/>
{/** 写成类的绑定函数,可以避免ref执行两次*/}
<input ref={this.refFunction} type="text" placeholder="点击按钮显示数据"/>
<button onClick={this.showData}>点击显示左侧数据</button>
</div>
);
}
}
一、如果 ref 回调函数是以"内联函数"的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。
这是因为在每次渲染时会创建一个新的函数实例。所以 React 清空旧的 ref 并且设置新的。这个时候会调用一次,此时传入的参数式null。之后等渲染的时候会再调用一次,
二、
通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题。 通过将方法绑定到类上这种方式,由于直接挂载到实例上了,就不需要重新生成函数,也就不需要重新执行了。
总结
- 尽量避免使⽤字符串形式的ref, 如果⾮要⽤, 也没什么问题
- 回调形式的ref有内联函数回调形式, 还有class函数回调形式, 没什么⼤影响,⽤内联即可。
- createRef⽐较复杂, 但是官⽅最为推荐。