[React] ref属性

简介

ref 即 reference ,是 React 提供给我们的安全访问 DOM 元素或者某个组件实例的句柄。
组件被调用时会新建一个该组件的实例,而 ref 就会指向这个实例。它可以是一个回调函数,这个回调函数会在组件被挂载后立即执行。
为了防止内存泄漏,当卸载一个组件的时候,组件里所有的 ref 都会变为 null。

目录

  • 简介
  • ref的创建
    • 类组件
    • 函数组件
  • ref作为属性
    • 类组件
      • ref属性是一个字符串(已废弃,不建议使用)
      • ref 属性是一个函数
      • ref属性是一个ref对象
    • 函数组件
  • ref作用
    • 受控组件与非受控组件
      • 受控组件
      • 非受控组件
    • useRef

ref的创建

所谓 ref 对象的创建,就是通过 React.createRef 或者 React.useRef 来在组件中创建一个 ref 原始对象,用来获取dom元素。

{
    current:null , // current指向ref对象获取到的实际内容,可以是dom元素,组件实例,或者其它。
}

类组件

在类组件上通过createRef创建ref对象。

class Test extends React.Component{
    constructor(props){
       super(props);
       this.currentRef = React.createRef(null);
    }
    componentDidMount(){
        console.log(this.currentRef);
    }
    render= () => <div ref={ this.currentRef } >createRef test</div>
}
export default Test;

在这里插入图片描述

createRef 就是创建了一个对象,对象上的 current 属性,用于保存通过 ref 获取的 DOM 元素,组件实例等。

// react/src/ReactCreateRef.js
export function createRef() {
  const refObject = {
    current: null,
  }
  return refObject;
}

createRef 一般用于类组件创建 ref 对象,可以将 ref 对象绑定在类组件实例上,这样更方便后续操作 ref
注意:不要在函数组件中使用 createRef,否则会造成 ref 对象内容丢失等情况。

函数组件

在函数组件中通过 useRef 来创建 ref 对象。

export default function Test() {
  const currentRef = React.useRef(null)
  React.useEffect(() => {
    console.log(currentRef.current)
  }, []);
  return <div ref={ currentRef } >useRef test</div>
}

在这里插入图片描述

useRef 底层逻辑是和 createRef 差不多,就是 ref 保存位置不相同。类组件有一个实例 instance 能够维护像 ref 这种信息,所以createRef是直接把ref对象存储在 instance 上的,而函数组件没有这种instance,如果在函数组件上使用createRef,那么每一次函数组件更新,所有变量都会重新声明,那么ref就会随之被重置,起不到保存的效果了,这就是函数组件为什么不能用 createRef 的原因。
为了解决这个问题,hooks 和函数组件对应的 fiber 对象建立起关联,将 useRef 产生的 ref 对象挂到函数组件对应的 fiber 上,函数组件每次执行,只要组件不被销毁,函数组件对应的 fiber 对象一直存在,所以 ref 等信息就会被保存下来。

ref作为属性

react 对 ref 的处理,主要表现在父子组件交互时,处理标签中的 ref 属性,以及转发 ref

类组件

ref属性是一个字符串(已废弃,不建议使用)

在类组件中,用字符串 ref 标记一个 DOM 元素或一个类组件,在react 在底层逻辑会判断类型,如果是 DOM 元素,会把真实 DOM 绑定在组件 this.refs (组件实例下的 refs )属性上,如果是类组件,会把子组件的实例绑定在 this.refs 上。

class Children extends React.Component {
  render = () => <div>hello,world</div>
}

export default class Test extends React.Component {
  componentDidMount() {
    console.log(this.refs) // 
  }
  render = () => <div>
    <div ref="currentDom">ref是字符串</div>
    <Children ref="currentComInstance" />
  </div>
}

在这里插入图片描述
上述代码中,取值时可以用this.ref.currentDomthis.ref.currentComInstance取到相应的元素。
这种方式已经废弃了,原因有以下几点:

  1. string ref 不可组合,如果第三方库的父组件已经给子组件传递了 ref,那么我们就无法再在子组件上添加 ref
  2. 回调引用没有一个所有者,因此您可以随时编写它们
  3. 不适用于Flow之类的静态分析
  4. string ref 强制React跟踪当前正在执行的组件

ref 属性是一个函数

class Children extends React.Component {
  render = () => <div>hello,world</div>
}

export default class Test extends React.Component {
  currentDom = null
  currentComponentInstance = null
  componentDidMount() {
    console.log(this.currentDom)
    console.log(this.currentComponentInstance)
  }
  render = () => <div>
    <div ref={(node) => this.currentDom = node}  >ref是函数</div>
    <Children ref={(node) => this.currentComponentInstance = node} />
  </div>
}

在这里插入图片描述
如上述代码所示,当用一个函数来标记 ref 的时候,将作为 callback 形式,等到真实 DOM 创建阶段,执行 callback ,获取的 DOM 元素或组件实例,再以回调函数第一个参数形式返回。所以可以像所以在上述代码中,用组件实例下的属性 currentDomcurrentComponentInstance 来接收真实 DOM 和组件实例。

ref属性是一个ref对象

创建ref对象,如第一部分所述,使用createRef

class Children extends React.Component {
  render = () => <div>hello,world</div>
}
export default class Test extends React.Component {
  currentDom = React.createRef(null)
  currentComponentInstance = React.createRef(null)
  componentDidMount() {
    console.log(this.currentDom)
    console.log(this.currentComponentInstance)
  }
  render = () => <div>
    <div ref={this.currentDom}>ref是对象</div>
    <Children ref={this.currentComponentInstance} />
  </div>
}

打印
以上三种方法都无法用在函数组件中,因为函数组件没有实例。

函数组件

不能在函数组件上直接使用 ref 属性,因为他们没有实例,这时需要用到forwardRef

import React, { useEffect, useRef } from 'react';

const Child = React.forwardRef((props, ref) => {
  return <input ref={ref}></input>
})

const Test = () => {
  const childrenRef = useRef();
  useEffect(() => {
    console.log(childrenRef.current); // child input
  }, [])
  return <Child ref={childrenRef}></Child>
}
export default Test;

在这里插入图片描述
React.forwardRef返回的是一个react组件,接受的参数是一个render函数,render(props, ref)。这个函数的第二个参数会将接收到的ref作为返回组件的ref属性,这就实现了ref的转发。

ref作用

其实ref是不推荐使用的,因为使用ref后,一些情况下元素会脱离react的控制。

受控组件与非受控组件

受控组件

受控组件指的是,在表单控件(例如input等),其值是受到react管理和控制的,即仅需要定义一个state和setState函数,在用户输入时UI和值都可以实时更新。

import React, { useState } from 'react';

function ControlledComponentExample() {
  const [inputValue, setInputValue] = useState('');

  const handleInputChange = (event) => {
    setInputValue(event.target.value);
  };

  return (
    <div>
      <label htmlFor="input">Enter something: </label>
      <input
        type="text"
        id="input"
        value={inputValue}
        onChange={handleInputChange}
      />
      <p>You typed: {inputValue}</p>
    </div>
  );
}

上述代码流程为,当input中值发生改变时,触发handleInputChange函数,从而将event.target.value更新到inputValue上,state的更新触发了页面的重新渲染,所以页面也更新了。
受控组件的特点时,数据流是单向的,均由state变化而触发,所以不应该强制赋值。

非受控组件

非受控组件即通过ref直接取dom元素中的值,而不是通过state控制它。

import React, { useRef } from 'react';

function UncontrolledComponentExample() {
  const inputRef = useRef(null);

  const handleButtonClick = () => {
    alert('Input value is: ' + inputRef.current.value);
  };

  return (
    <div>
      <label htmlFor="uncontrolledInput">Enter something: </label>
      <input
        type="text"
        id="uncontrolledInput"
        ref={inputRef}
      />
      <button onClick={handleButtonClick}>Get Input Value</button>
    </div>
  );
}

点击按钮的时候,handleButtonClick函数中直接通过ref拿取input中的值,不通过react自身的state变换。

useRef

在函数组件中,useRef 返回一个可变的 ref 对象,返回的 ref 对象在函数组件的整个生命周期内保持不变。所以 useRef 可以很方便地保存任何可变值。

import { useState, useEffect, useRef } from "react";

export default function Test() {
  const ref = useRef(false)
  const [a, setA] = useState(0);

  useEffect(() => {
    if (!ref.current) {
      ref.current = true
    } else {
      //do some
    }
  }, [a]);
  return ('');
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/377052.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言笔试题之实现C库函数 pow()(递归的思想)

实例要求&#xff1a; 1、请你实现C库函数 pow()&#xff08;stdio.h & math.h&#xff09; &#xff0c;即计算 x 的整数 n 次幂函数&#xff08;即x^n &#xff09;&#xff1b;2、函数声明&#xff1a;double myPow(double x, int n)&#xff1b;参数&#xff1a;1、x …

2019年江苏省职教高考计算机技能考试——一道程序改错题的分析

题目&#xff1a;函数将str字符串中的5个数字字符串转换为整数&#xff0c;并保存在二维数组m的最后一行&#xff0c;各元素为3、-4、16、18、6。并经函数move处理后&#xff0c;运行结果如下&#xff1a; 18 6 3 -4 16 16 18 6 3 -4 -4 16 …

Springboot整合JUnit5框架

目录 第一章、在pom文件中导入依赖第二章、新建测试类第三章、新建测试方法 友情提醒: 先看文章目录&#xff0c;大致了解文章知识点结构&#xff0c;点击文章目录可直接跳转到文章指定位置。 第一章、在pom文件中导入依赖 SpringBoot2.2x之后的版本中spring-boot-starter-te…

如何使用phpStudy搭建网站并结合内网穿透远程访问本地站点

文章目录 [toc]使用工具1. 本地搭建web网站1.1 下载phpstudy后解压并安装1.2 打开默认站点&#xff0c;测试1.3 下载静态演示站点1.4 打开站点根目录1.5 复制演示站点到站网根目录1.6 在浏览器中&#xff0c;查看演示效果。 2. 将本地web网站发布到公网2.1 安装cpolar内网穿透2…

2023年全球软件开发大会(QCon上海站2023):核心内容与学习收获(附大会核心PPT下载)

在信息化和全球化日益加速的今天&#xff0c;软件开发技术日新月异&#xff0c;对全球各行各业产生了深远影响。2023年全球软件开发大会&#xff08;QCon上海站2023&#xff09;无疑成为行业内外瞩目的焦点。本次大会汇集了全球顶级的软件开发专家、企业领袖、研究者&#xff0…

1978-2022年各省家庭恩格尔系数(分城镇、农村)

1978-2022年各省家庭恩格尔系数&#xff08;分城镇、农村&#xff09; 1、时间&#xff1a;1978-2022年 2、指标&#xff1a;城镇家庭恩格尔系数、农村家庭恩格尔系数 3、来源&#xff1a;统计年鉴、省统计公报 4、范围&#xff1a;31省 5、指标解释&#xff1a;恩格尔系数…

sqli-labs-master靶场训练笔记(38-53|boss战)

2024.2.4 level-38 &#xff08;堆叠注入&#xff09; 这题乍一看感觉又是来卖萌的&#xff0c;这不是和level-1一模一样吗 然后仔细看了一下源代码&#xff0c;根据 mysqli_multi_query 猜测这题的本意应该是堆叠注入 mysqli_multi_query() 是 PHP 中用于执行多个 SQL 查…

Docker-Learn(一)使用Dockerfile创建Docker镜像

1.创建并运行容器 编写Dockerfile&#xff0c;文件名字就是为Dockerfile 在自己的工作工作空间当中新建文件&#xff0c;名字为Docerfile vim Dockerfile写入以下内容&#xff1a; # 使用一个基础镜像 FROM ubuntu:latest # 设置工作目录 WORKDIR /app # 复制当前目…

堆排序-Python实现

简述 堆排序&#xff08;Heap Sort&#xff09;是一种基于比较的排序算法&#xff0c;它利用堆这种数据结构所设计的一种排序算法。堆排序是一种选择排序&#xff0c;它的最坏&#xff0c;最好&#xff0c;平均时间复杂度均为O(nlogn)&#xff0c;它也是不稳定排序。 堆 堆排…

力扣精选算法100道——和为 K 的子数组[前缀和专题]

和为K的子数组链接 目录 第一步&#xff1a;了解题意​编辑 第二步&#xff1a;算法原理 第三步&#xff1a;代码 第一步&#xff1a;了解题意 数组中和为k的连续子数组&#xff0c;我们主要关注的是连续的&#xff0c; 比如[1,1,1],和为2的子数组有俩个&#xff0c;比如第…

搜索与图论(一)(深搜,广搜,树与图的存储遍历,拓扑排序)

一、DFS 往深里搜&#xff0c;搜到叶子结点那里&#xff0c;回溯&#xff0c;到可以继续到叶子结点深搜的位置。 1、回溯一定要恢复现场 2、定义一个与当前递归层数有关的终止条件&#xff08;题目要求的东西&#xff09; 3、每层都用循环判断是否存在可以dfs的路 输出数字…

flutter使用qr_code_scanner扫描二维码

qr_code_scanner仓库地址&#xff1a;qr_code_scanner | Flutter Package 需要添加android和ios的相机权限和本地相册权限&#xff1a; android中添加权限: 在android\app\build.gradle中修改&#xff1a;minSdkVersion 20 并且在android/app/src/main/AndroidManifest.xml中…

Kafka系列(一)【消息队列、Kafka的基本概念、Kafka的工作机制、Kafka可满足的需求、Kafka的特性、Kafka的应用场景】

kafka系列 一 一、消息队列1. 消息队列的来源2. 什么是消息队列3. 消息队列主要有哪些作用 二、Kafka的基本概念代理、生产者、消费者、消费者组主题、分区、副本、记录 三、了解 Kafka的工作机制-生产消息/消费消息四、Kafka可满足的需求五、Kafka的特性六、Kafka的场景 转自《…

漏洞挖掘 | 任意密码重置 + 存储型XSS

本文由掌控安全学院 - 老板来一份烧鹅饭 投稿 还是老样子&#xff0c;打开谷歌镜像&#xff0c;搜索site:edu.cn指定域名&#xff0c;搭配关键字登陆&#xff0c;注册&#xff0c;忘记密码&#xff0c;等等&#xff0c;或者xxx系统比较容易挖出通杀。 逻辑漏洞挖掘思路 1.登陆…

大带宽服务器托管的特点和考虑因素

很多公司和企业对于使用大带宽服务器的需求和存储不一样&#xff0c;为了满足不同的用户需求&#xff0c;大带宽服务器托管是个不错的选择&#xff0c;小编为您整理发布大带宽服务器托管的特点和要考虑的因素。 大带宽服务器托管是一种服务器托管服务&#xff0c;其主要特点是…

【数据分享】1929-2023年全球站点的逐年平均降水量(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、湿度等指标&#xff0c;说到常用的降水数据&#xff0c;最详细的降水数据是具体到气象监测站点的降水数据&#xff01; 有关气象指标的监测站点数据&#xff0c;之前我们分享过1929-2023年全…

【图形学】投影和消隐简介

投影 正交投影 对于物体上任意一点的三维坐标P(x,y,z),投影后的三维坐标为 P ′ ( x ′ , y ′ , z ′ ) P^\prime(x^\prime,y^\prime,z^\prime) P′(x′,y′,z′),那么正交投影的方程为 { x ′ x y ′ y z ′ 0 \begin{cases} x^\primex\\y^\primey\\z^\prime0 \end{case…

2 月 7 日算法练习- 数据结构-并查集

并查集 并查集是一种图形数据结构&#xff0c;用于存储图中结点的连通关系。 每个结点有一个父亲&#xff0c;可以理解为“一只伸出去的手”&#xff0c;会指向另外一个点&#xff0c;初始时指向自己。 一个点的根节点是该点的父亲的父亲的的父亲&#xff0c;直到某个点的父亲…

【buuctf--来首歌吧】

用 Audacity 打开&#xff0c;左声道部分可以放大&#xff0c;可以按照长短转换成摩斯密码&#xff0c;放大后&#xff1a; ..... -... -.-. ----. ..--- ..... -.... ....- ----. -.-. -... ----- .---- ---.. ---.. ..-. ..... ..--- . -.... .---- --... -.. --... ----- -…

2024 年改变行业的人工智能主要趋势

1、导读 当我们迈入 2024 年时&#xff0c;了解人工智能趋势至关重要。它们不仅仅涉及技术进步&#xff1b;还涉及技术进步。它们意味着我们解决问题、做出决策和展望未来的方式发生了转变。本文旨在探索这些变革趋势&#xff0c;并强调人工智能如何不断突破可能性的界限&…
最新文章