React---refs

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

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

1. refs适用的场景

  • 手动控制DOM节点
  • 获取子组件的尺寸或者实例方法
  • 和第三方DOM库集成(典型的jPlayer)

注意:不要滥用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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • refs提供了可以在render方法中访问dom节点和创建的react元素的方法。 为了良好的阅读体验,请查看gi...
    mytac阅读 438评论 0 1
  •   在常规的 React 数据六中,props 是父组件与子组件交互的唯一方法。如果需要改子元素,你需要用新的pr...
    果汁凉茶丶阅读 978评论 0 0
  • It's a common pattern in React to wrap a component in an ...
    jplyue阅读 3,295评论 0 2
  • 有效的人生,无法违背的倾向性,在伦理道德法律范围内争取做更多,只有这辈子更好的自己,下辈子循环反复……
    心安_913c阅读 360评论 0 0
  • 2018年6月3日 星期日 天晴 文|墨凉 图|网络 我轻轻的告诉你,你今天真好看! 喂!你昨晚是失眠...
    十翰墨凉阅读 2,806评论 9 11