美文网首页
React---refs

React---refs

作者: Waitingforyu | 来源:发表于2019-03-10 18:57 被阅读0次

    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

    相关文章

      网友评论

          本文标题:React---refs

          本文链接:https://www.haomeiwen.com/subject/ufaupqtx.html