如果我们需要通过一些操作获取某些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对象
再创建一个类组件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推荐使用对象或者是函数
使用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);
}
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函数执行了两次,因为这个函数是写在行间的,每次渲染都会触发。
行间函数形式,会因为页面重新先渲染导致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
能够使用属性和状态进行控制,就不要使用ref。