一、ref
1、ref基础
如果我们需要通过一些操作获取某些dom元素怎么办呢?
如点击按钮使得输入框对焦,用js的document
当然可以,但是react中提供了更好地方法ref
。
import React, { Component } from 'react'
export default class Comp extends Component {
handleClick = () => {
console.log(this);
this.refs.txt.focus();
console.log('input获取焦点');
}
render() {
return (
<div>
<input ref="txt" type="text" name="" id=""/>
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
}
使用ref设置一个类似id的标记,就可以通过这个标记找到该dom元素。
我们查看输出的this就知道,ref中就有了我们设置的txt标记。
注意:ref并不是一个属性哦
ref作用于内置的html组件,得到的将是真实的dom对象
2、ref标记一个组件
再创建一个类组件A,并在Comp中使用A并标记。
import React, { Component } from 'react'
class A extends Component {
method() {
console.log("这是类组件A的method方法");
}
render() {
return <h1>类组件A</h1>
}
}
export default class Comp extends Component {
handleClick = () => {
console.log(this);
this.refs.txt.focus();
console.log('input获取焦点');
}
render() {
return (
<div>
<input ref="txt" type="text" name="" id=""/>
<button onClick={this.handleClick}>按钮</button>
<A ref="compA" />
</div>
)
}
}
我们发现,Comp中this就已经能够得到组件A的信息了。
甚至通过ref标记,我们能使用类组件A中的方法,
export default class Comp extends Component {
handleClick = () => {
console.log(this);
this.refs.txt.focus();
console.log('input获取焦点');
this.refs.compA.method();
}
render() {
return (
<div>
<input ref="txt" type="text" name="" id=""/>
<button onClick={this.handleClick}>按钮</button>
<A ref="compA" />
</div>
)
}
}
ref作用于类组件,得到的将是类的实例
为甚这里说是类组件呢?我们来试试函数组件。
import React, { Component } from 'react'
class A extends Component {
method() {
console.log("这是类组件A的method方法");
}
render() {
return <h1>类组件A</h1>
}
}
function B(){
return <h1>函数组件B</h1>
}
export default class Comp extends Component {
handleClick = () => {
console.log(this);
this.refs.txt.focus();
console.log('input获取焦点');
this.refs.compA.method();
}
render() {
return (
<div>
<input ref="txt" type="text" name="" id=""/>
<button onClick={this.handleClick}>按钮</button>
<A ref="compA" />
<B ref="compB" />
</div>
)
}
}
函数组件只是返回一个react元素dom,所以react认为给函数组件ref没有什么意义。但是函数组件内部可以使用ref。
function B(){
return <h1 ref="b">函数组件B</h1>
}
ref不能作用于函数组件
到此为止,是不是觉得ref很好用呢?但是:
ref不再推荐使用字符串赋值,字符串赋值的方式将来可能会被移出
目前,ref推荐使用对象或者是函数
3、ref使用对象
使用React.createRef()
import React, { Component } from 'react'
export default class Comp extends Component {
constructor(props) {
super(props);
this.txt = React.createRef();
console.log(this.txt);
}
render() {
return (
<div>
<input ref="txt" type="text" name="" id=""/>
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
}
此时我们已经成功创建了一个ref,但是因为我们并没有进行绑定某个元素,所以目前它为空
import React, { Component } from 'react'
export default class Comp extends Component {
constructor(props) {
super(props);
this.txt = React.createRef();
console.log(this.txt);
}
render() {
return (
<div>
<input ref={this.txt} type="text" name="" id=""/>
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
}
当我们帮顶了input元素后,render时就会对txt赋值。
import React, { Component } from 'react'
export default class Comp extends Component {
constructor(props) {
super(props);
this.txt = React.createRef();
console.log(this.txt);
}
handleClick = () => {
console.log(this);
this.txt.current.focus();
console.log('input获取焦点');
}
render() {
return (
<div>
<input ref={this.txt} type="text" name="" id=""/>
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
}
使用时,由于是一个对象,所以要以对象属性的方式调用。
以下的书写方式也是毫无问题的
constructor(props) {
super(props);
this.txt = {
current: null
};
console.log(this.txt);
}
4、ref使用函数
1.行间函数
import React, { Component } from 'react'
export default class Comp extends Component {
handleClick = () => {
this.txt.focus();
console.log('input获取焦点');
}
render() {
return (
<div>
<input ref={el => {
console.log('ref函数');
this.txt = el;
}} type="text"/>
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
}
那么该函数是如何执行,何时执行的呢?
export default class Comp extends Component {
handleClick = () => {
this.txt.focus();
console.log('input获取焦点');
}
componentDidMount() {
console.log("didMount", this.txt);
}
render() {
return (
<div>
<input ref={el => {
console.log('ref函数');
this.txt = el;
}} type="text" />
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
}
实际上ref函数会在componentDidMount生命周期时自动执行赋值。
export default class Comp extends Component {
handleClick = () => {
this.txt.focus();
this.setState({});
console.log('input获取焦点');
}
componentDidMount() {
console.log("didMount", this.txt);
}
render() {
return (
<div>
<input ref={el => {
console.log('ref函数',el);
this.txt = el;
}} type="text" />
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
}
一旦组件状态发生改变,组件重新渲染,发现我们的ref函数执行了两次,因为这个函数是写在行间的,每次渲染都会触发。
2.声明调用
行间函数形式,会因为页面重新先渲染导致ref函数重新执行,我们就可以用函数保存,然后每次直接调用同一个函数。
import React, { Component } from 'react'
export default class Comp extends Component {
handleClick = () => {
this.txt.focus();
this.setState({});
console.log('input获取焦点');
}
componentDidMount() {
console.log("didMount", this.txt);
}
getRef = el => {
console.log("ref函数", el);
this.txt = el;
}
render() {
return (
<div>
<input ref={this.getRef} type="text" />
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
}
此时就能看到,我们的ref函数并没有因为状态改变而重新执行。
此外,组件卸载时,也会执行ref函数。(即ref以来的元素变化了,ref函数就会执行)
import React, { Component } from 'react'
export default class Comp extends Component {
state = {
show: true
}
handleClick = () => {
this.setState({
show: !this.state.show
});
}
componentDidMount() {
console.log("didMount", this.txt);
}
getRef = el => {
console.log("ref函数", el);
this.txt = el;
}
render() {
return (
<div>
{
this.state.show && <input ref={this.getRef} type="text" />
}
<button onClick={this.handleClick}>显示/隐藏</button>
</div>
)
}
}
组件被卸载,ref函数触发。
总结:
- componentDidMount的时候会调用该函数
- 在componentDidMount事件中可以使用ref
- 如果ref的值发生了变动(旧的函数被新的函数替代),分别调用旧的函数以及新的函数,时间点出现在componentDidUpdate之前
- 旧的函数被调用时,传递null
- 新的函数被调用时,传递对象
- 如果ref所在的组件被卸载,会调用函数
谨慎使用ref
能够使用属性和状态进行控制,就不要使用ref。
- 调用真实的DOM对象中的方法
- 某个时候需要调用类组件的方法
博主开始运营自己的公众号啦,感兴趣的可以关注“飞羽逐星”微信公众号哦,拿起手机就能阅读感兴趣的博客啦!