ref(reference)引用
场景: 希望直接使用dom元素中的某个方法(如focus),或者希望直接使用自定义组件中的某个方法
- ref作用于内置的html组件,得到的将会是真实的Dom对象
- ref作用于类组件,得到将是类的实例
- ref不能作用于函数组件
render() {
return (
// this.refs.input 得到的是dom对象
<input ref='input' />
)
}
render() {
return (
// this.refs.input 得到的是实例对象
<MyClassComponent ref='input' />
)
}
ref不再推荐使用如上JS代码中的字符串赋值形式 未来可能会移除这种赋值方式
目前, ref推荐使用对象或者函数
对象
通过 React.createRef 创建
import React, { Component } from 'react'
export default class RefClass extends Component {
constructor(props) {
super(props)
this.input = React.createRef()
}
render() {
return (
// this.input = { current: Dom对象 }
// 使用: this.input.current.xxx
<div ref={this.input}></div>
)
}
}
函数
- 将函数直接写在ref里
该函数的执行时机是 componentDidMount, 此时可以使用 this.ele (ps: 本例中)
当页面重新渲染时,该函数会调用两次, 执行的时机是componentDidUpdate
第一次调用传入null 本例中打印 ‘调用了’ null
第二次传入dom对象 本例中打印 ‘调用了’ inputDom元素
import React, { Component } from 'react'
export default class Ref extends Component {
handleClick = () => {
console.log('this', this)
this.setState({})
}
render() {
return (
<div>
<input ref={el=> {
console.log('调用了', el)
this.ele = el
}} type="text" />
<button onClick={this.handleClick}>点击吧</button>
</div>
)
}
}
- 将函数定义到类里
调用时机跟之前相同, 不过该函数不会因为页面渲染而重新调用,只会执行一次
import React, { Component } from 'react'
export default class Ref extends Component {
handleClick = () => {
console.log('this', this)
this.setState({})
}
getInputRef = el => {
console.log('调用了', el)
this.ele = el
}
render() {
return (
<div>
<input ref={this.getInputRef} type="text" />
<button onClick={this.handleClick}>点击吧</button>
</div>
)
}
}
ref转发 (forwardRef)
- 函数组件
App.js
import React, { Component } from 'react'
// 2. 被转发的函数组件必须有第二个参数来接受被转发的ref
function A(props, ref) {
return (
// 3. 此时myRef指向h1元素
<h1 ref={ref}>组件A</h1>
)
}
// 1. 通过React.forwardRef可以转发ref, 参数传递***函数***组件,返回一个组件
const NewA = React.forwardRef(A)
export default class App extends Component {
myRef = React.createRef()
componentDidMount() {
// 4. 得到想要的h1元素
console.log('myRef', this.myRef)
}
render() {
return (
<div>
<NewA ref={this.myRef}/>
</div>
)
}
}
- 如果类组件也想做转发,可以使用如下方法,当然还有别的方法
import React, { Component } from 'react'
// 3. 使用约定好的属性forwardRef绑定到h1元素
class A extends React.Component {
render() {
return (
<h1 ref={this.props.forwardRef}>类组件A</h1>
)
}
}
// 1. 通过React.forwardRef可以转发ref, 参数传递***函数***组件,返回一个组件
const NewA = React.forwardRef((props, ref) => {
// 2. 约定一个自定义属性名(本例forwardRef)用于转发ref
return <A {...props} forwardRef={ref} />
})
export default class App extends Component {
myRef = React.createRef()
componentDidMount() {
// 4. 得到想要的h1元素
console.log('myRef', this.myRef)
}
render() {
return (
<div>
<NewA ref={this.myRef}/>
</div>
)
}
}
- 当我们使用高阶组件的时候绑定ref,如果高阶组件不做处理,那么ref将指向这个高阶组件,明显不对,可以使用ref转发完成这种场景的问题
高阶组件withLog.js
import React from 'react'
export default function withLog(Comp) {
class LogContainer extends React.Component {
componentDidMount() {
console.log(`组件: ${Comp.name}被创建了`)
}
componentWillUnmount() {
console.log(`组件: ${Comp.name}被销毁了`)
}
render() {
// 解构属性
const { forwardRef, ...rest } = this.props
return (
// 属性传递
<Comp ref={forwardRef} {...rest} />
)
}
}
return React.forwardRef((props, ref)=> {
// 约定forwardRef用于转发ref
return <LogContainer {...props} forwardRef={ref} />
})
}
App.js
import React, { Component } from 'react'
import { A } from './components/testHOC'
import withLog from './HOC/withLog'
// 高阶组件
const AComp = withLog(A)
export default class App extends Component {
myRef = React.createRef()
componentDidMount() {
// 经过高阶组件处理,现在myRef将指向A组件
console.log('myRef', this.myRef)
}
render() {
return (
<div>
<AComp ref={this.myRef} isLogin a='a' />
</div>
)
}
}