React Ref

385 阅读2分钟

一、Ref的创建和访问

1、React.createRef()

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef()
  }
  
  componentDidMount() {
    console.log(this.myRef.current)
  }

  render() {
    return <div ref={this.myRef} />  
  }
}

2、回调Ref

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
  }
  
  componentDidMount() {
    console.log(this.myRef)
  }

  render() {
    return <div ref={el => this.myRef = el} />  
  }
}

3、useRef(Hook 函数组件写法)

funtion MyComponent() {
  const myRef = useRef(null)
  
  useEffect(() => {
    console.log(myRef.current)
  }, [])

  render() {
    return <div ref={myRef} />  
  }
}

二、组件间传递Ref(父组件访问子组件的Ref)

1、回调Ref

父组件将 refs 回调函数当作 inputRef props 传递给了子组件,子组件将相同的函数作为特殊的 ref 属性传递给 <input> 标签。父组件中的 this.inputElement 会被设置为与子组件中的 input 元素相对应的 DOM 节点。

function ChildComp(props) {
  return <input ref={props.inputRef} />
}

class ParentComp extends React.Component {
  
  componentDidMount() {
    console.log(this.inputElement) // <input />
  }

  render() {
    return <ChildComp inputRef={el => this.inputElement = el}/>
  }
}

2、React.forwardRef()

  1. 父组件通过调用 React.createRef 创建一个 React ref 并将其赋值给 ref 属性。

  2. 父组件通过 ref 属性向下传递属性 <ChildComp ref={ref}> 给子组件。

  3. 子组件使用 React.forwardRef 包裹,并接受一个函数作为参数,函数参数的第二个为 ref。

  4. 子组件向下转发该 ref 参数到 <button ref={ref}>

  5. 当父组件的 ref 挂载完成,ref.current 将指向子组件的 <button> DOM 节点。

const ChildComp = React.forwardRef((props, ref) => (
  <button ref={ref}>
    {props.children}
  </button>
));

class ParentComp extends React.Component {
  constructor(props) {
    super(props)
    this.buttonRef = React.createRef()
  }
  
  componentDidMount() {
    console.log(this.buttonRef) // <button />
  }

  render() {
    return <ChildComp ref={this.buttonRef}/>
  }
}

3、在使用redux环境下,父组件(class)调用子组件(class)的方法

import React from 'react'

// 父组件
class ParentComp extends React.Component {
   
  onRef = (ref) => {
    this.childRef = ref
  }
  
  handleChange = () => {
    this.childRef.onChangeCount()) // '子组件处理逻辑'
  }

  render() {
    return (
      <div>
        <button onClick={this.handleChange}>改变子组件的属性</button>
        <ChildComp onRef={this.onRef} />
      </div>
    )
  }
}
import React from 'react'

// 子组件
class ChildComp extends React.Component {
  state = {
    count: 0
  }

  componentDidMount(){
    // 传递子组件实例
    this.props.onRef(this)
  }
  
  onChangeCount = () => {
    // ... other logic
    this.setState({ count: this.state.count + 1 }
    console.log('子组件处理逻辑')
  }
  
  render () {
    return (
      <div>当前值:{this.state.count}</div>
    )
  }
}

const mapStateToProps = state => ({})
// Note: forwardRef parameter is supported in >= v6.0 only
const Module = connect(mapStateToProps, null, null, { withRef: true })(ChildComp)
export default Module

4、在使用redux环境下,父组件(class)调用子组件(hooks)的方法

import React from 'react'

// 父组件
class ParentComp extends React.Component {
  constructor(props) {
    super(props)
    this.buttonRef = React.createRef()
  }
  
  handleChange = () => {
    this.buttonRef.current.onChangeCount()) // '子组件处理逻辑'
  }

  render() {
    return (
      <div>
        <button onClick={this.handleChange}>改变子组件的属性</button>
        <ChildComp ref={this.buttonRef}/>
      </div>
    )
  }
}
import React, { useState, useImperativeHandle } from 'react'

// 子组件
const ChildComp = (props) => {
  const { innerRef } = props
  const [count, setCount] = useState(0)
  
  useImperativeHandle(innerRef, () => ({
    // 暴露给父组件的属性/方法
    count,
    onChangeCount,
  }))
  
  const onChangeCount = () => {
    // ... other logic
    setCount(prevCount => prevCount + 1)
    console.log('子组件处理逻辑')
  }
  
  return (
    <div>当前值:{count}</div>
  )
};

const mapStateToProps = state => ({})
// Note: This parameter is supported in >= v6.0 only
const Module = connect(mapStateToProps, null, null, { forwardRef: true })(ChildComp)
export default React.forwardRef((props, ref) => <Module {...props} innerRef={ref} />)