学习 React 的 ref 操作

732 阅读3分钟

1、React中的ref是什么

refReact提供的用来操纵React组件实例或者DOM元素的接口

2、ref的使用里程

React v16.3 之前,ref 通过字符串(string ref)或者回调函数(callback ref)的形式进行获取,在v16.3 中,经0017-new-create-ref 提案引入了新的React.createRef API

ref通过字符串的形式将要被移除,通过回调函数这个方式也不推荐,最好是使用React.createRef方法。

3、ref的使用

对于字符串是形式的使用和回调函数方式的使用就不写了。

在这里重点学习第三种方式 React.createRef(),先说一下 React.createRef()的原理。

在react源码中ReactRef的代码如下:

import type {RefObject} from 'shared/ReactTypes';

export function createRef(): RefObject {
  const refObject = {
    current: null,
  };
  return refObject;
}

这个源码很简单,就只是创建了一个对象并返回出来了。当使用 this.value = React.createRef()这个时,就是将这个返回的对象赋值给这个定义的变量。此时的this.value={current:null},为以后的真实DOM和类的实例的赋值奠定基础。

如果在类组件上添加ref属性的话,ref.current给的是类的实例(new 类名);如果在原生dom中加了ref属性,这个ref.current指向的原生的真实的dom

(1)在类组件中使用REF

import React from 'react';
import ReactDom from 'react-dom';
class RefUseByClass extends React.Component{
    constructor(props){
        super(props);
        this.classInputValue = React.createRef();
    }
    render(){
        return(
            <input ref={this.classInputValue}/>
        )
    }
}
class GetClassInputFocus extends React.Component{
    constructor(props){
        super(props);
        this.getFocusInput = React.createRef();
    }
    getFocus =(event)=>{
       //所以在这里,this.getFocusInput.current就是子组件的实例(new RefUseByClass),
        //而子组件实例中返回的input也有ref,而这个ref指向的是真实的input这个dom结构,
        //所以说      this.getFocusInput.current.classInputValue.current获取到的是input,
        // 进而对这个input框进行操作
        this.getFocusInput.current.classInputValue.current.focus();
    }
    render(){
        return(
            <React.Fragment>
            //在这个父组件中调用子组件,当父组件进行render的时候,是将子组件的实例赋给了ref.current属性
                <RefUseByClass ref={ this.getFocusInput}/>
                <button onClick={this.getFocus}>类组件使用ref实现点击按钮输入框聚焦</button>
            </React.Fragment>
        )
    }
}
ReactDom.render(
    <GetClassInputFocus/>,
    document.getElementById('root')
)

(2)在函数组件中使用ref

import React from 'react';
import ReactDom from 'react-dom';
//在这里使用函数组件,注意查看不同之处
function  RefUseByClass(props,ref){
    return(
        <input ref={ref}/>
    )
}
// React.forwardRef() 实现ref的转发
// 因为函数组件没有实例,函数一执行完就销毁了,而ref制定的是原生dom或者类的实例,所以这个函数组件是不能给定ref属性
// 如果想要给函数组件添加一个ref属性的话,就需要到了React.forwardRef()这个转发站

//将函数组件传递给这个React.forwardRef()转发站,返回的是一个封装好子组件的类组件,
//当在父组件中进行render()的时候    <ForwardRef > 组件就会执行这个返回来的类组件,
//在这个执行类组件是这个子组件也开始运行,并将子组件里的返回的真实的dom返回给ref,
//所以在点击函数getFocus里      this.getFocusInput.current指的是子组件中真实的dom,即input框。

let ForwardRef = React.forwardRef(RefUseByClass);
class GetClassInputFocus extends React.Component{
    constructor(props){
        super(props);
        this.getFocusInput = React.createRef();
    }
    getFocus =(event)=>{
        this.getFocusInput.current.focus();
    }
    render(){
        return(
            <React.Fragment>
                <ForwardRef ref={ this.getFocusInput}/>
                <button onClick={this.getFocus}>函数组件使用ref实现点击按钮输入框聚焦</button>
            </React.Fragment>
        )
    }
}
ReactDom.render(
    <GetClassInputFocus/>,
    document.getElementById('root')
)

对比一下下面的代码就知道 React.forwardRef 是怎么个意思了:

import React from 'react';
import ReactDom from 'react-dom';
function  RefUseByClass(props,ref){
    return(
        <input ref={ref}/>
    )
}
// 方式一
function forwardRef( func ){
    return class  extends React.Component{
        render(){
            return func(this.props,this.props. ref2)
        }
     }
}
// 方式二
// function forwardRef( func ){
//     return props=>func( props,props.ref2)
// }
let ForwardRef = forwardRef(RefUseByClass);
class GetClassInputFocus extends React.Component{
    constructor(props){
        super(props);
        this.getFocusInput = React.createRef();
    }
    getFocus =(event)=>{
        this.getFocusInput.current.focus();
    }
    render(){
        return(
            <React.Fragment>
                <ForwardRef ref2={ this.getFocusInput}/>
                <button onClick={this.getFocus}>函数组件使用ref实现点击按钮输入框聚焦</button>
            </React.Fragment>
        )
    }
}
ReactDom.render(
    <GetClassInputFocus/>,
    document.getElementById('root')
)

自己的学习记录,如果有误,请指出,谢谢!