如何深入理解React的ref 属性

 更新时间:2021年05月15日 10:09:49   作者:lovin  
关于 Refs ,React 官网讲解的对于新手来说不太友好,还是自己一字一句解读后并以代码验证的方式后真正理解的.

概述

首先,Refs 和 ref 是两个概念,Refs 是 React 提供的可用特定 API 创建的一个对象。该对象的结构如下:

这个对象只有一个属性就是 current ,那么这个对象是用来干嘛的呢?

Refs 允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。(DOM节点就是指原生DOM元素,在render()方法中创建的 React 元素就是指 React 的类组件元素)

我们可以想象这样一个需求,两个兄弟元素,一个是 div ,一个是 button。现在想实现点击 button,改变 div 的背景颜色。在原生的 DOM 技术中,我们可以在 button 的点击函数里使用 document.querySelector('xxx') 的方式选中 div 节点,然后改变其背景样式。但是无论是在 Vue 还是 React 这样的框架中,页面元素都是动态生成的,无法使用 DOM API 获取的方式。而且 React 中大部分操作的元素不是 原生DOM元素,而是 React 元素。 那么如何选择到某一个 原生DOM元素 或者 React 元素呢?

其实,理论上,我们不需要进行任何的选择操作,这样会失去前端框架中组件独立的概念。一般情况下是通过 组件通信 的方式进行事件的处理的。上述的情况可以使用 EventBus 的方式进行组件通信,button 的点击事件中进行自定义事件的触发,在 div 中进行自定义事件的监听,让 button 以事件通知的方式告知 div 让其改变背景色,而不是在 button 的事件中直接获取 div 进行操作。

但是 React 为我们提供了直接访问 DOM元素 和 React 元素的方式,就是通过 Refs。使用的方式很简单,就是,为想要访问的元素上添加 ref 属性,将 Refs 对象附加到 ref 属性上,那么此时 Refs 对象的 current 属性就不再是空,而是对应的 DOM元素 或 React 元素实例了。

1. Refs 对象的创建

在 React 中,创建 Ref 对象的方式有两种:

1.1 React.createRef()

使用 React.createRef() 的方式可以创建一个 Ref 对象,可通过附加到 ref 属性上访问一个 原生DOM元素 或者 class 组件。
这种方式既可以在函数组件中使用,也可以在class组件中使用。

1.2 React.useRef(initialValue)

在 React 16.8 中新增了 Hook 后,又多了一个可以创建 Ref 对象的 Hook。即 React.useRef(initialValue)。
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。
这种方式只能在函数组件中使用。

2. ref 属性的使用

ref 属性只能被添加到 原生DOM元素 或者 React的 class 组件上。不能在 函数组件 上使用 ref 属性,因为函数组件没有实例。

若想在函数组件上使用 ref 属性,可以通过 React.forwardRef 将 Ref 转发到函数组件内部的 原生 DOM 元素上。

2.1 为原生DOM元素添加 ref

class类组件内部

class App extends React.Component{
    constructor(props){
        super(props)
        this.myRef = React.createRef()
    } 
    componentDidMount(){
        console.log(this.myRef)
        console.log(this.myRef.current)
    }
    render(){
        return (
            <div ref={this.myRef}>我是App组件</div>
        )
    }
}

函数组件内部

const App = ()=>{
    const myRef = React.useRef(null)
    //const myRef = React.createRef() 两种创建 ref 对象的方式都可以
    React.useEffect(()=>{
        console.log(myRef)
        console.log(myRef.current)
    },[]) //模拟生命周期
    return (
        <div ref={myRef}>我是函数组件内部使用ref的div</div>
    )
}

2.2 为class组件添加 ref

class ClassChild extends React.Component{
    render(){
        return (
            <div>我是App组件的 class 子组件 ClassChild</div>
        )
    }
}

class App extends React.Component{
    constructor(props){
        super(props)
        this.myRef = React.createRef()
    } 
    componentDidMount(){
        console.log(this.myRef)
        console.log(this.myRef.current)
    }
    render(){
        return (
            <ClassChild ref={this.myRef}/>
        )
    }
}

2.3 为class组件转发的原生DOM元素添加 ref

ref 转发原理就是将父组件中定义的 ref 对象当作普通属性的方式传递给子组件,然后子组件通过 props 接收再赋值给自己 DOM元素 上。

class ClassChild extends React.Component{
    render(){
        return (
            <div ref={this.props.refProp}>我是App组件的 class 子组件 ClassChild</div> //添加了 ref
        )
    }
}

class App extends React.Component{
    constructor(props){
        super(props)
        this.myRef = React.createRef()
    } 
    componentDidMount(){
        console.log(this.myRef)
        console.log(this.myRef.current)
    }
    render(){
        return (
            <ClassChild refProp={this.myRef}/> //作为普通属性传递
        )
    }
}

2.4 为函数组件转发的原生DOM元素添加 ref

根据class类组件转发的原理,我想到的实现方法如下:

const FunChild = (props)=>{
    return (
        <div ref={props.refProp}>我是函数组件 FunChild</div>
    )
}
class App extends React.Component{
    constructor(props){
        super(props)
        this.myRef = React.createRef()
    } 
    componentDidMount(){
        console.log(this.myRef)
        console.log(this.myRef.current)
    }
    render(){
        return (
            <FunChild refProp={this.myRef}/>
        )
    }
}

这种实现方式是可以的,但这不是在 函数组件 上直接使用 ref 属性的方式,React 提供了在函数组件上直接使用 ref 的方式,就是使用 React.forwardRef 创建 React 元素。

React.forwardRef

const FunChild = React.forwardRef((props, ref)=>{
    return (
        <div ref={ref}>我是函数组件 FunChild</div>
    )
}) // 使用 React.forwardRef 改造函数组件
class App extends React.Component{
    constructor(props){
        super(props)
        this.myRef = React.createRef()
    } 
    componentDidMount(){
        console.log(this.myRef)
        console.log(this.myRef.current)
    }
    render(){
        return (
            <FunChild ref={this.myRef}/>  //直接给函数组件传递 ref
        )
    }
}

感觉 React.forwardRef 就是把 ref 属性单独从 props 中抽离出来了。
尽管上述方式实现了在函数组件上使用 ref 属性,但此时的 Ref 对象是访问的函数组件内部的 原生DOM元素 或其他 class组件。也就是说,在这里函数组件只是起到了一个转发的作用。

3. 回调 Refs

上述的方式中,我们都是通过创建一个 Ref 对象,通过 ref 属性的方式挂载到 原生DOM元素 或者 class 组件上用于访问该元素或实例。
实际上,ref 属性除了可以接收一个 Ref 对象外,还可以接收一个回调函数。
当 ref 属性接收 Ref 对象时,会将其对应的 DOM元素 或者 class组件实例 直接赋值给 Ref 对象中的 current 属性上。而当 ref 属性接收一个回调函数时,会将其对应的 DOM元素 或 class组件实例作为回调函数的参数调用回调函数。
因此我们可以通过回调 Refs 的方式不依靠 Ref 对象,更灵活地控制要访问的元素或实例。

class App extends React.Component{
    constructor(props){
        super(props)
        this.myRef = null
        this.setMyRef = (element)=>{
            this.myRef = element
        }
    } 
    componentDidMount(){
        console.log(this.myRef)
    }
    render(){
        return (
            <div ref={this.setMyRef}>我是App组件</div>
        )
    }
}

以上就是如何深入理解React的ref 属性的详细内容,更多关于深入理解React的ref 属性的资料请关注脚本之家其它相关文章!

相关文章

  • React如何实现浏览器打印部分内容详析

    React如何实现浏览器打印部分内容详析

    这篇文章主要给大家介绍了关于利用React如何实现浏览器打印部分内容的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用React具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-05-05
  • 一文详解如何React中实现插槽

    一文详解如何React中实现插槽

    这篇文章主要为大家详细介绍了如何在React中实现插槽,文中的示例代码讲解详细,对我们的学习或工作具有一定的借鉴价值,需要的可以了解一下
    2023-03-03
  • React组件内事件传参实现tab切换的示例代码

    React组件内事件传参实现tab切换的示例代码

    本篇文章主要介绍了React组件内事件传参实现tab切换的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-07-07
  • react-router4按需加载(踩坑填坑)

    react-router4按需加载(踩坑填坑)

    这篇文章主要介绍了react-router4按需加载(踩坑填坑),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-01-01
  • 一文教会你用redux实现computed计算属性

    一文教会你用redux实现computed计算属性

    在computed中,可以定义一些属性,即计算属性,下面这篇文章主要给大家介绍了关于如何利用redux实现computed计算属性的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-05-05
  • useReducer使用详解及其应用场景

    useReducer使用详解及其应用场景

    这篇文章主要介绍了useReducer使用详解及其应用场景,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • 如何在 React 中调用多个 onClick 函数

    如何在 React 中调用多个 onClick 函数

    这篇文章主要介绍了如何在React中调用多个onClick函数,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2023-11-11
  • 从零开始搭建webpack+react开发环境的详细步骤

    从零开始搭建webpack+react开发环境的详细步骤

    这篇文章主要介绍了从零开始搭建webpack+react开发环境的详细步骤,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • 提高React界面性能的十个技巧

    提高React界面性能的十个技巧

    众所周知,性能是Web应用界面的关键方面,它直接影响到用户的使用体验。本文将向您展示十种提高React UI性能的特定技术和一般方法。
    2021-05-05
  • React高阶组件使用教程详解

    React高阶组件使用教程详解

    高阶组件就是接受一个组件作为参数并返回一个新组件(功能增强的组件)的函数。这里需要注意高阶组件是一个函数,并不是组件,这一点一定要注意,本文给大家分享React 高阶组件HOC使用小结,一起看看吧
    2022-12-12

最新评论