React ref 是 React 中一个用于访问组件中 DOM 元素或者类实例的方式。它允许我们直接操作 DOM,而不需要通过 state 或 props 来更新组件。
过时 API:String 类型的 Refs
在最最早期的时候,React 中 Ref 的用法非常简单,类似于 Vue,给一个字符串类型的值,之后在方法中通过 this.refs.xxx 就能够引用到。
示例如下:
import React, { Component } from 'react'
export default class App extends Component {
clickHandle = () => {
console.log(this);
console.log(this.refs.inputRef);
this.refs.inputRef.focus();
}
render() {
return (
<div>
<input type="text" ref="inputRef"/>
<button onClick={this.clickHandle}>聚焦</button>
</div>
)
}
}
在上面的代码中,我们在 input 上面挂了一个 ref 属性,对应的值为 inputRef,之后查看组件实例,可以看到该组件实例中的 refs 里面就保存了该 input 的 DOM 元素。
然后我们就可以像之前一样进行 DOM 元素的操作了。例如在上面的示例中我们进行了聚焦的操作。
但是这里需要注意两点:
- 避免使用 refs 来做任何可以通过声明式实现来完成的事情
- 该 API 已经过时,可能会在未来的版本被移除,官方建议我们使用回调函数或 createRef API 的方式来代替
参阅官网 https://zh-hans.reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs
至于为什么 String 类型的 Refs 会被废弃,主要是以下几个方面原因:
参阅地址:https://github.com/facebook/react/pull/8333#issuecomment-271648615
createRef API
接下来我们来看一下官方推荐的 createRef API。
import React, { Component } from 'react'
export default class App extends Component {
constructor(props) {
super();
this.inputRef = React.createRef();
console.log(this.inputRef); // {current: null}
}
clickHandle = () => {
console.log(this.inputRef); // {current: input}
this.inputRef.current.focus();
}
render() {
return (
<div>
<input type="text" ref={this.inputRef}/>
<button onClick={this.clickHandle}>聚焦</button>
</div>
)
}
}
在上面的代码中,我们创建 Ref 不再是通过字符串的形式,而是采用的 createRef 这个静态方法创建了一个 Ref 对象,并在组件实例上面新增了一个 inputRef 属性来保存这个 Ref 对象。
createRef 这个方法本质也很简单,就是返回了一个 {current: null} 的对象,下面是 createRef 的源码:
最后我们把这个对象和 input 进行关联。
如果要获取 DOM 元素,可以通过 this.inputRef.current 来获取。
除了在 JSX 中关联 Ref,我们还可以直接关联一个类组件,这样就可以直接调用该组件内部的方法。例如:
// 子组件
import React, { Component } from 'react'
export default class ChildCom1 extends Component {
test = () => {
console.log("这是子组件的 test 方法");
}
render() {
return (
<div>ChildCom1</div>
)
}
}
// 父组件
import React, { Component } from 'react';
import ChildCom1 from "./components/ChildCom1"
export default class App extends Component {
constructor(props) {
super();
this.comRef = React.createRef();
}
clickHandle = () => {
console.log(this);
console.log(this.comRef); // {current: ChildCom1}
this.comRef.current.test();
}
render() {
return (
<div>
{/* ref 关联子组件 */}
<ChildCom1 ref={this.comRef}/>
<button onClick={this.clickHandle}>触发子组件方法</button>
</div>
)
}
}
虽然提供这种方式,但这是一种反模式,相当于回到了 jQuery 时代,因此尽量避免这么做。
React.createRef API 是在 React 16.3 版本引入的,如果是稍早一点的版本,官方推荐使用回调 Refs,也就是函数的形式。例如:
import React, { Component } from 'react';
import ChildCom1 from "./components/ChildCom1"
export default class App extends Component {
constructor() {
super();
this.inputRef = element => {
this.inputDOM = element;
};
this.comRef = element => {
this.comInstance = element;
};
}
clickHandle = () => {
this.inputDOM.focus();
this.comInstance.test();
}
render() {
return (
<div>
{/* ref 关联子组件 */}
<input type="text" ref={this.inputRef} />
<ChildCom1 ref={this.comRef} />
<div>
<button onClick={this.clickHandle}>聚焦并且触发子组件方法</button>
</div>
</div>
)
}
}
注意
函数组件不能使用 ref 属性。因为函数组件没有实例。可以通过 useRef 使用 ref。