React---refs

2019-03-10  本文已影响0人  Waitingforyu

Refs提供了一个访问render()方法内DOM节点或者ReactNode的方法

典型的React数据流中,props是父组件和子组件交互的唯一手段。要修改一个子组件,就需要使用新的props重新渲染它。然而,确实存在少数一些情况需要命令性地(imperatively)修改一个子节点而不是通过典型的props数据流方式。被修改的子节点可能是一个React组件实例(比如调用某个子组件的实例方法),亦或是一个DOM元素(比如手动地控制某个input标签聚焦)。对于这两种情况,React都提供了各种处理方法,refs就是其中的一种。

1. refs适用的场景

注意:不要滥用refs,比如:在使用antd的<Modal />时,可以直接通过修改props.visible为true或false即可实现Modal组件的显示和隐藏,则大可不必使用该组件的show()、hide()方法

2. 创建Refs

注意:Ract 16.3引入的API React.createRef()。比较旧的React版本,保留了refs关键字。不管是新旧版本,都建议使用refs回调方式(最后的有对应的示例)。本文主要实现一个页面加载时,Input组件自动聚焦,并且在点击Button组件时聚焦Input组件的功能,使用的方法为React.createRef()。github代码库中有对应的新旧版本实现方式。

  1. refs通过React.createRef()创建,使属性ref附加到React元素上。Refs通常在 一个组件构造时赋值给一个实例属性,这样在整个组件中他们都可以被引用到。
  2. 创建以后,通过React.createRef().current方法获取。

根据节点类型的不同,ref的值也不同:

  • 如果ref用在HTML元素上,构造函数中通过React.createRef()创建的ref会将原生DOM元素放到它的current属性中。
  • 如果ref用在自定义组件类型上,ref使用它的current属性指向所挂载的组件实例。
  • 函数式组件上不能使用ref,因为它们没有实例。

3. DOM中创建与使用

class Input extends React.Component {
    constructor(props) {
        super(props)
        // 在构造方法内初始化
        this.inputRef = React.createRef()
    }

    componentDidMount() {
        // 使用.current调用
        this.inputRef.current.focus();
    }
    
    // Input的实例方法
    focus = () => {
        if(this.inputRef.current) this.inputRef.current.focus();
    }

    render() {
        return (
            <div className="block">
                <p>Input 加载时自动聚焦</p>
                <input ref={this.inputRef} />
            </div>
        )
    }
}

组件挂载时,React会将ref的current属性设置成DOM元素,卸载时,再把ref的current属性设置为null。ref更新发生在componentDidMount或者componentDidUpdate生命周期回调之前。

4. 自定义组件中创建与使用

import React from 'react'
import Button from './Button'
import Input from './Input'

class Ref extends React.Component {
    constructor(props) {
        super(props)
        // 初始化 获取挂载的组件Input实例
        this.inputComponentRef = React.createRef()
    }

    
    handleClick = () => {
        // 调用Input实例的方法
        if(this.inputComponentRef.current) this.inputComponentRef.current.focus()
    }

    render() {
        return (
            <div>
                <Button onClick={this.handleClick} />
                <Input ref={this.inputComponentRef} />
            </div>
        )
    }
}

export default Ref

同DOM中使用类似,组件挂载时,React会将ref的current属性设置成组件的实例,卸载时,再把ref的current属性设置为null。ref更新发生在componentDidMount或者componentDidUpdate生命周期回调之前。

5. 函数式组件无法为当前组件直接创建refs

const Input = () => <input />

class App extends React.Component {
  constructor(props) {
    super(props);
    this.inputComponentRef = React.createRef();
  }
  
  render() {
    // 不起作用,会报错
    return (
      <Input ref={this.inputComponentRef} />
    );
  }
}

但是,函数式组件内部可以使用ref引用属性使其指向一个DOM元素或者一个类组件,例如:

const Input = (props) => {
    let inputRef = React.createRef();

    function handleClick() {
      inputRef.current.focus();
    }

    return (
      <div>
        <input
         type="text"
          ref={inputRef} />

        <button
          onClick={handleClick}
        >Focus</button>
      </div>
    )
}

6. 使用回调的方式 (推荐)

在需要声明ref的位置绑定一个方法,返回的参数是DOM节点或则实例组件,组件在加载时会自动触发该回调方法,该参数作为实例的一个属性在其他位置直接使用即可。


class Input extends React.Component {
    constructor(props) {
        super(props)
    }

    componentDidMount() {
        // 不需要使用current调用
        this.inputRef && this.inputRef.focus();
    }

    initRef = (ele) => {
        // 组件加载时(或者更新时)自动触发该方法
        this.inputRef = ele
    }
    
    focus = () => {
        if(this.inputRef) this.inputRef.focus();
    }

    
    render() {
        return (
            <div className="block">
                <p>Input 加载时自动聚焦</p>
                <input ref={this.initRef} />
            </div>
        )
    }
}

export default Input

使用引用回调函数的注意事项
如果ref回调函数定义在内联函数(inline function)中,更新时他会被调用两次,第一次参数是null,第二次参数才是DOM元素。这是因为每个渲染都会创建一个新的函数实例,所以React需要清除旧的引用并设置新的。你可以通过将引用回调定义为该类的绑定方法来避免这种情况,但请注意,大多数情况下这样做或者不这样做都没太大关系。

7. React低版本遗留的API:字符串引用Refs

绑定一个 字符串类型的ref 属性到 render 的返回值上

<input ref="myInput" />

在其他位置(实例方法或者生命周期函数中)使用

componentDidMount() {
  // 保留关键字this.refs
  // 页面加载完成时使input标签自动聚焦
  this.refs.myInput.focus()
}

8. 建议使用其他的解决方案替代refs

在极少的一些情况下,我们需要从父组件中访问某个子DOM节点或者子组件的一些属性和方法。一般来说不建议这么做,因为它打破了组件封装,但是它偶尔也很有用,比如触发获取焦点,或者测量一个子DOM节点的尺寸或者位置。
本文代码链接地址:https://github.com/zhiyuanMain/ReactForJianshu.git

上一篇下一篇

猜你喜欢

热点阅读