React和Vue一样,不建议操作DOM,但是如果我们在某个场景必须操作DOM,那么也可以使用refs来实现。

refs的基本使用


refs的创建

refs是通过React.createRef()来创建的,通过使用ref属性来对应某个元素。一般在constructor中赋值给某个实例属性来达到多次复用的作用

class App extends React.Component{
    render(){
        return <MyComponent />
    }
}

class MyComponent extends React.Component{
    constructor(props){
        super(props);
        // 创建ref
        this.myRef=React.createRef();
        // 绑定方法
        this.btnClick=this.btnClick.bind(this);
    }
    btnClick(){
        // 打印ref对应的元素
        // 元素被放在ref的current属性中
        console.log(this.myRef.current);
    }
    render(){
        return <button ref={this.myRef} onClick={this.btnClick}>123</button>
    }
}

上面的代码,点击按钮就会打印出对应的元素
在这里插入图片描述

访问refs

当refs被传递给render中的元素的时候,可以通过current属性来访问,如同上面的代码一样

	btnClick(){
        // 打印ref对应的元素
        // 元素被放在ref的current属性中
        console.log(this.myRef.current);
    }

ref的current属性根据其放在不同的标签上有所不同

  1. 当放在html元素上时,current属性指向html元素
  2. 当放在自定义标签时,current属性指向挂载的实例

上面的代码就是第一种情况,对于第二种情况,修改上面的代码

class App extends React.Component{
    render(){
        return <MyComponent />
    }
}

class MyComponent extends React.Component{
    constructor(props){
        super(props);
        // 创建ref
        this.myRef=React.createRef();
        // 绑定方法
        this.btnClick=this.btnClick.bind(this);
    }
    btnClick(){
        // 打印ref对应的元素
        // 元素被放在ref的current属性中
        console.log(this.myRef.current);
    }
    render(){
        return <div>
                <button onClick={this.btnClick}>123</button>
                {/* ref指向自定义组件 */}
                <ChildComponent ref={this.myRef} />
                </div>
    }
}

class ChildComponent extends React.Component{
    render(){
        return <div>111</div>
    }
}

点击后发现和刚才结果不同
在这里插入图片描述
打印出来的变成了自定义组件的实例
因为ref的current指向的是组件的实例,和函数组件没有实例,所以无法将ref用在函数组件上
将刚才的自定义组件ChildComponent改成下面这个函数组件

function ChildComponent(props){
    return <div>111</div>
}

控制台会出现报错如下
在这里插入图片描述
但是我们还是可以函数组件内部使用ref的

function ChildComponent(props){
    let textRef=React.createRef();
    function consoleRef(){
        console.log(textRef.current);
    }
    return <div>
        <p ref={textRef}>123</p>
        <button onClick={consoleRef}>btn</button>
    </div>
}

这里能用是理所当然的,这和是函数组件或者是class组件没有关系,ref是指向html元素的。

传递DOM refs


ref指向子组件

通过在子组件上面使用ref属性指向React.createRef()创建的refs来达到将DOM从子组件传递到父组件的目的

render(){
    return <div>
            <button onClick={this.btnClick}>123</button>
            {/* ref指向自定义组件 */}
            <ChildComponent ref={this.myRef} />
            </div>
}

然而,这种方法并非理想的解决方法,我们最终只能得到子组件的实例,而非DOM,更何况上面也说了,函数组件无法使用该方法。

refs转发

refs转发通过React.forwardRef来创建一个子组件,并获取父组件的ref,将内部的元素中的ref属性指向传递来的ref,达到将DOM传递到父组件的目的

function ChildComponent(props){
    let textRef=React.createRef();
    function consoleRef(){
        console.log(textRef.current);
    }
    return <div>
        {/* 获取子组件中的refs */}
        <RefBtn ref={textRef} />
        <button onClick={consoleRef}>clickbtn</button>
    </div>
}

const RefBtn = React.forwardRef((props,ref)=>{
    // ref属性指向父组件传下来的ref
    return <button ref={ref}>
            refBtn
        </button>
})

点击clickBtn,会打印出button的DOM
在这里插入图片描述
React.forwardRef生成的组件在react-devtools中被当成一个组件
在这里插入图片描述

refs回调


refs回调是另一种设置refs的方法,不同于直接设置值,refs回调是通过传递一个方法,在方法中完成对refs的赋值,与之前不同的是,refs回调可以直接将元素赋值给refs,这样就不用多用一步去调用current属性了

class MyComponent extends React.Component{
    constructor(props){
        super(props);
        // 创建ref
        this.myRef=null;
        this.setMyRef=element=>{
            this.myRef=element;
        }
        // 绑定方法
        this.btnClick=this.btnClick.bind(this);
    }
    btnClick(){
        // 打印ref对应的元素
        // 打印的不再是current属性
        if(this.myRef)console.log(this.myRef);
    }
    componentDidMount(){
        this.btnClick()
    }
    render(){
        return <div>
                    <button onClick={this.btnClick}>123</button>
                    <div ref={this.setMyRef}>div</div>
                </div>
    }
}
Logo

前往低代码交流专区

更多推荐