[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 = nullcurrentComponentInstance = nullcomponentDidMount() {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><inputtype="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><inputtype="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.mzph.cn/news/672635.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C生万物】C语言分支和循环语句

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有…

京东云StarDB for openGauss实现混合多云场景元数据安全可控

推进产业数字化&#xff0c;构筑数智供应链技术底座&#xff0c;绕不开全球信息技术基础三大件之一的数据库。以技术创新为手段&#xff0c;以满足未来业务需求为目标&#xff0c;京东云自主研发的分布式数据库StarDB在长期的业务实践过程中不断进化&#xff0c;在海量且复杂的…

深入理解网络编程之BIO和NIO

目录 原生JDK网络编程BIO BIO通信模型服务端代码 BIO通信模型客户端代码 伪异步模型服务端代码&#xff08;客户端跟之前一致&#xff09; 原生JDK网络编程NIO 什么是NIO&#xff1f; NIO和BIO的主要区别 阻塞与非阻塞IO NIO之Reactor模式 NIO中Reactor模式的基本组成…

零基础学Python之网络编程

1.什么是socket 官方定义&#xff1a; 套接字&#xff08;socket&#xff09;是一个抽象层&#xff0c;应用程序可以通过它发送或接收数据&#xff0c;可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中&#xff0c;并与网络中的其他应用…

ChatGPT论文指南|ChatGPT论文写作过程中6个润色与查重提示词

论文完成初稿之后&#xff0c;一般情况下&#xff0c;宝子们还需要找专家给我们提出评审意见。找专家评审其实并不容易&#xff0c;即使对老师来说&#xff0c;找人评审论文也是一件苦活。我们这个时候可以通过文字提示让 ChatGPT充当我们的评审专家&#xff0c;为论文提出问题…

【操作系统·考研】I/O管理概述

1.I/O设备 1.1 块设备 信息交换以数据块为单位&#xff0c;它属于有结构设备。 块设备传输速率较高&#xff0c;可寻址&#xff0c;且可对该设备随机地的读写。 栗子&#x1f330;&#xff1a;磁盘。 1.2 字符设备 信息交换以字符为单位&#xff0c;属于无结构类型。 字符…

电力负荷预测 | 基于LSTM、TCN的电力负荷预测(Python)

文章目录 效果一览文章概述源码设计参考资料效果一览 文章概述 电力负荷预测 | 基于LSTM、TCN的电力负荷预测(Python) 源码设计 #------------------

一文彻底搞懂Redis的三种集群模式

文章目录 1. 主从模式1.1 基本原理1.2 工作机制1.3 优缺点 2. 哨兵模式2.1 基本原理2.2 工作机制2.3 优缺点 3. 集群模式3.1 基本原理3.2 工作机制3.3 优缺点 4. 总结 redis高可用的三种模式&#xff1a;主从模式&#xff0c;哨兵模式&#xff0c;集群模式。 所谓的高可用&…

如何修改远程端服务器密钥

前言 一段时间没改密码后&#xff0c;远程就会自动提示CtrlAltEnd键修改密码。但我电脑是笔记本&#xff0c;没有end键。打开屏幕键盘按这三个键也没用。 解决方法 打开远程 1、远程端WINC 输入osk 可以发现打开了屏幕键盘 2、电脑键盘同时按住CtrlAlt&#xff08;若自身电…

回溯法解决P2089 烤鸡问题太香啦,组合问题就用回溯法就对了!

学回溯的第二天&#xff0c;发现之前做过的一道洛谷的枚举题也可以用回溯法去解决&#xff0c;还是相当滴nice的。 先来看看leetcode上的这两道题 216.组合总和III 题目链接&#xff1a;216. 组合总和 III 思路就是比组合问题多了一个和为n的限制&#xff0c;大体还是可以…

嵌入式系统设计师

系列文章目录 1.元件基础 2.电路设计 3.PCB设计 4.元件焊接 5.板子调试 6.程序设计 7.算法学习 8.编写exe 9.检测标准 10.项目举例 11.职业规划 文章目录 第一章 计算机系统基础1、数值转换数的转换数据的存储单位 第一章 计算机系统基础 1、数值转换 数的转换 数据的存储…

解决“使用Edge浏览器每次鼠标点击会出现一个黑色边框”的问题

目录 一 问题描述 二 解决方案 三 方案来源 四 参考资料 & AI工具 一 问题描述 为了方便进行收藏夹同步&#xff0c;开始从Chrome浏览器切换到Edge浏览器。在使用Edge浏览器过程中发现“每次鼠标点击会出现一个黑色边框”&#xff08;效果如下图所示&#xff09;&#…

去照片背景的方法有哪些?分享这些简单的方法给你

在忙碌的日常工作中&#xff0c;不少朋友经常需要整理大量的图片素材。有时候&#xff0c;我们需要将那些带有背景色的图片转换成透明素材&#xff0c;这样在其他场合使用起来会更加方便。那么&#xff0c;有没有一种简便的方法&#xff0c;能将图片的背景去掉&#xff0c;轻松…

重学Java 13.面向对象.1

在熟悉的事物中循环 ——24.2.7 一、static关键字 1.static关键字的介绍以及基本使用 1.概述&#xff1a;static是一个静态关键字 2.使用&#xff1a; a.修饰一个成员变量&#xff1a; static 数据类型 变量名 b.修饰一个方法&#xff1a; 修饰符 static 返回值类型 方法名&am…

【办公技巧】如何设置Word文档部分内容无法编辑?

工作中&#xff0c;我们可能会在word中制作一些请柬、表格之类的&#xff0c;有些文件内容不想要进行修改&#xff0c;为了防止他人随意修改内容。我们可以设置限制编辑&#xff0c;可以对一部分内容设置限制编辑&#xff0c;具体方法如下&#xff1a; 我们将需要将可以编辑的…

作业2.6

一、填空题 1、一个类的头文件如下所示&#xff0c;num初始化值为5&#xff0c;程序产生对象T&#xff0c;且修改num为10&#xff0c;并使用show()函数输出num的值10。 #include <iostream.h> class Test { private: static int num; public: Test(int); void sh…

【华为 ICT HCIA eNSP 习题汇总】——题目集13

1、以下在项目规划阶段中需要完成的工作是&#xff08;&#xff09;。 A、确定技术方案 B、了解项目背景 C、选择网络产品 D、规划 IP 地址 考点&#xff1a;网络规划与设计 解析&#xff1a;&#xff08;B&#xff09; 确定技术方案是在网络规划的设计阶段完成的工作&#xff…

#Z2322. 买保险

一.题目 二.思路 1.暴力 训练的时候&#xff0c;初看这道题&#xff0c;这不就打个暴力吗&#xff1f; 2.暴力代码 #include<bits/stdc.h> #define int long long using namespace std; int n,m,fa,x,y,vis[1000001],ans; vector<int> vec[1000001]; void dfs(i…

如何实现高效的Web自动化测试?

随着互联网的快速发展&#xff0c;Web应用程序的重要性也日益凸显。为了保证Web应用程序的质量和稳定性&#xff0c;Web自动化测试成为必不可少的一环。然而&#xff0c;如何实现高效的Web自动化测试却是一个值得探讨的课题。 首先&#xff0c;选择合适的测试工具是关键。市面…

Android 13.0 原生SystemUI下拉通知栏每条通知默认展开

1.前言 在13.0的系统rom原生开发中,在在对SystemUI下拉通知栏做定制的时候,在下拉状态栏的时候,通知栏中最后一条通知默认是收缩的 点击按钮 就会展开 原生系统systemui就是如此,为了更美观 所以要求最后一条通知也默认展开,显得更美观 最终效果图: 2.原生SystemUI下拉通…