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。