如何深入理解React的ref 属性

概述

首先,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 属性的资料请关注我们其它相关文章!

(0)

相关推荐

  • React Native中的RefreshContorl下拉刷新使用

    我们知道App中都有下拉加载,在React Native中也有类似的控件 一.属性方法 (1) onRefresh function 在视图开始刷新的时候调用 (2) refreshing bool 视图是否在刷新时显示指示器,也表明当前是否在刷新中 (3) colors [ColorPropType] android平台适用 进行设置加载进去指示器的颜色,至少设置一种,最多可以设置4种 (4) enabled bool android平台适用 用来设置下拉刷新功能是否可用 (5) progre

  • React组件refs的使用详解

    ref顾名思义我们知道,其实它就可以被看座是一个组件的参考,也可以说是一个标识.作为组件的属性,其属性值可以是一个字符串也可以是一个函数. 其实,ref的使用不是必须的.即使是在其适用的场景中也不是非用不可的,因为使用ref实现的功能同样可以转化成其他的方法来实现.但是,既然ref有其适用的场景,那也就是说ref自有其优势.关于这一点和ref的适用场景,官方文档中是这样说的: 在从 render 方法中返回 UI 结构之后,你可能想冲出 React 虚拟 DOM 的限制,在 render 返回的

  • 详解react-refetch的使用小例子

    使用react-refetch来简化api获取数据的代码 const List = ({data: gists}) => { return ( <ul> {gists.map(gist => ( <li key={gist.id}>{gist.description}</li> ))} </ul> ) } const withData = url => Part => { return class extends Component

  • React ref的使用示例

    写了一段时间的 react,99%都在写 state.prop.useState.useEffect,对 ref 特别不熟悉,前几天做一个需求,想用 ref 实现父组件捞子组件的某个状态值,结果失败了,特此整理一下 ref 相关内容. 什么是 ref 官网介绍: 在典型的 React 数据流中,props 是父组件与子组件交互的唯一方式.要修改一个子组件,你需要使用新的 props 来重新渲染它.但是,在某些情况下,你需要在典型数据流之外强制修改子组件.被修改的子组件可能是一个 React 组件

  • React三大属性之Refs的使用详解

    refs是React中用来取得某个JSX组件或者某个DOM中的一些状态值的时候,用来获取节点的方法.在React官方的解释中,它的适用范围如下: 管理焦点,文本选择或媒体播放. 触发强制动画. 集成第三方 DOM 库. React文档中再三强调,请不要过度使用refs,所以当我们可以用dom原生对象解决时,尽量不要使用refs 依照之前的写法,首先是给出类组件和函数组件中refs的写法 类组件 在类中,refs有三种方式,目前最常用的是回调的形式使用,分别进行演示 //直接定义refs,已废弃

  • React中useRef的具体使用

    相信有过React使用经验的人对ref都会熟悉,它可以用来获取组件实例对象或者是DOM对象. 而useRef这个hooks函数,除了传统的用法之外,它还可以"跨渲染周期"保存数据. 首先来看一下它传统的用法: import React, { useState, useEffect, useMemo, useRef } from 'react'; export default function App(props){ const [count, setCount] = useState(

  • React中的refs的使用教程

    ref是React中的一种属性,当render函数返回某个组件的实例时,可以给render中的某个虚拟DOM节点添加一个ref属性,如下面的代码所示: <body> <script type="text/jsx"> var App = React.createClass({ render: function() { return ( <div> <input type="text" placeholder="inp

  • 学习React中ref的两个demo示例

    为了摆脱繁琐的Dom操作, React提倡组件化, 组件内部用数据来驱动视图的方式,来实现各种复杂的业务逻辑 ,然而,当我们为原始Dom绑定事件的时候, 还需要通过组件获取原始的Dom, 而React也提供了ref为我们解决这个问题. 为什么不能从组件直接获取Dom? 组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM).只有当它插入文档以后,才会变成真实的 DOM 如果需要从组件获取真实 DOM 的节点,就要用到官方提供的ref属性 使

  • React中Ref 的使用方法详解

    本文实例讲述了React中Ref 的使用方法.分享给大家供大家参考,具体如下: React中Ref 的使用 React v16.6.3 在典型的React数据流中,props是父组件与其子组件交互的唯一方式.要修改子项,请使用new props 重新呈现它.但是,在某些情况下,需要在典型数据流之外强制修改子项.要修改的子项可以是React组件的实例,也可以是DOM元素.对于这两种情况,React都提供了api. 何时使用refs refs有一些很好的用例: 1.文本选择或媒体播放. 2.触发势在

  • 如何深入理解React的ref 属性

    概述 首先,Refs 和 ref 是两个概念,Refs 是 React 提供的可用特定 API 创建的一个对象.该对象的结构如下: 这个对象只有一个属性就是 current ,那么这个对象是用来干嘛的呢? Refs 允许我们访问 DOM 节点或在 render 方法中创建的 React 元素.(DOM节点就是指原生DOM元素,在render()方法中创建的 React 元素就是指 React 的类组件元素) 我们可以想象这样一个需求,两个兄弟元素,一个是 div ,一个是 button.现在想实

  • 深入理解React 三大核心属性

    目录 1.State属性 2.Props 属性 3.Refs 属性 (1)字符串形式 (2)函数回调形式 (3)createRef创建ref容器 1.State 属性 React 把组件看成是一个状态机(State Machines).通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致. React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM). import React from 'react '; import Reac

  • React的三大属性你都知道吗

    目录 React三大属性 props 函数组件 类组件 state 有状态组件和无状态组件 setState props和state属性的区别 refs React.createRef ref的绑定 总结 React三大属性 props 组件是封闭的,接收外部数据应该通过props来实现 函数组件通过参数props来接收数据,props是一个对象: 类组件通过this.props接收数据. 传递数据:在组件标签上添加属性 函数组件 const Hello = (props) => { conso

  • React组件三大属性之state,props,refs

    目录 1.1基本理解和使用 1.1.1 使用React开发者工具调试 1.1.2 定义组件的方式 1.1.3 注意 1.1.4 渲染类组件标签的基本流程 1.2 组件实例的三大核心属性之一:state 1.2.1 理解 1.2.2 案例 1.2.3 在类式组件使用state 1.2.4 在类式组件使用state的简写方式 1.2.3 强烈注意 1.3 组件实例的三大核心属性之一:props 1.3.1 理解 1.3.3 作用 1.3.4 在类式组件使用props 1.3.5 在函数式组件使用pr

  • 深入理解React Native核心原理(React Native的桥接(Bridge)

    在这篇文章之前我们假设你已经了解了React Native的基础知识,我们会重点关注当native和JavaScript进行信息交流时的内部运行原理. 主线程 在开始之前,我们需要知道在React Native中有三个主要的线程: shadow queue:负责布局工作 main thread:UIKit 在这个线程工作(译者注:UI Manager线程,可以看成主线程,主要负责页面交互和控件绘制的逻辑) JavaScript thread:运行JS代码的线程 另外,一般情况下每个native模

  • 深入理解React调度(Scheduler)原理

    目录 异步调度 时间分片 异步调度原理 总结 异步调度 问题:由于对于大型的 React 应用,会存在一次更新,递归遍历大量的虚拟 DOM ,造成占用 js 线程,使得浏览器没有时间去做一些动画效果,伴随项目越来越大,项目会越来越卡. 对比Vue: Vue 有这 template 模版收集依赖的过程,轻松构建响应式,使得在一次更新中,Vue 能够迅速响应,找到需要更新的范围,然后以组件粒度更新组件,渲染视图. React 中,一次更新 React 无法知道此次更新的波及范围,所以 React 选

  • React state状态属性详细讲解

    目录 1. 基本使用 2. 使用setState操作state数据 3. 案例-toDoList 4. 案例-购物车 1. 基本使用 要点: 成员属性 state 它是一个特殊的属性,它是当前类的私有数据,只有在当前的组件中才能操作里面的数据 状态( state )即数据,是组件内部的私有数据,只能在组件内部使用,和vue中data差不多,不过它没有像vue中的data进行了数据劫持 state的值是对象,表示一个组件中可以有多个数据 通过this.state来获取状态,react 中没有做数据

  • React state状态属性用法讲解

    目录 类组件中的state setState的用法 类组件如何限制state更新视图 setState原理揭秘 函数组件中的state useState的用法 如何监听state的变化 setState(dispatch)更新特点 useState的注意事项 总结 相同点 不同点 类组件中的state setState的用法 React项目中UI改变来源于state的改变,类组件中setState是更新组件,渲染视图的主要方式 基本用法 setState(obj,callback) 第一个参数:

随机推荐