React学习笔记六-refs

news2024/11/25 7:42:22

此文章是本人在学习React的时候,写下的学习笔记,在此纪录和分享。此为第六篇,主要介绍react中的refs。

目录

1.refs基本使用

1.1字符串类型ref小案例

2.回调形式的ref

2.1回调形式ref小案例

2.2回调ref中调用次数问题

3.createRef

3.1createRef的使用

 3.2createRef的“专人专用”

4.ref总结


1.refs基本使用

组件内的标签可以定义ref属性来标识自己。

1.1字符串类型ref小案例

首先我们写一个小案例,来了解refs。我们写一个自定义组件,写两个输入框,点击按钮提示第一个输入框的值,当第二个文本框失去焦点的时候,提示第二个文本框的值。

步骤如下:

        1.在render函数构建虚拟dom时,我们使用ref为input框添加标识,第一个input叫做input1,第二个叫input2,比如说

        <input ref='input1' type="text" placeholder="点击按钮提示数据"/>

注意,使用的是ref来添加表示,而不是refs,不要多写一个s。

        2.然后我们为相应的按钮,和第二个input框设置事件,分别是点击事件和失去焦点事件。如下:

        <button onClick={this.showData}>  <input ref='input2' onBlur={this.showData2} type="text"/>

注意在react中的事件与js中不同,比如说点击事件onclick,在react里面on后的首字母大写,与js做区分。基本上在react里面事件都是在on后首字母大写。

        3.下一步我们写事件处理函数,showData和showData2。由于我们给input框设置了ref添加标识,那么所以使用ref添加标识的信息,全部存储在本组件实例的refs属性里面,所以我们在组件内使用的时候,直接this.refs.使用ref设置的标识名即可使用,也比较推荐使用解构赋值来使用。如下,对refs属性解构赋值,拿取我们想要的input1:

        const {input1} = this.refs

       如此这个inpput1就代表了第一个输入框,我们就可以对第一个文本框的内容进行输入。

代码如下:

<script type="text/babel">
        //创建组件
        class Demo extends React.Component {
            showData = ()=>{
                const {input1} = this.refs
                alert(input1.value)
            }
            showData2 = ()=>{
                const {input2} = this.refs
                alert(input2.value)
            }
            render() {
                return (//构建虚拟dom
                    <div>
                        <input ref='input1' type="text" placeholder="点击按钮提示数据"/>&nbsp;
                        <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                        <input ref='input2' onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
                    </div>
                )
            }

        }

        //渲染组件到界面
        ReactDOM.render(<Demo/>, document.getElementById('test1'))
    </script>

2.回调形式的ref

2.1回调形式ref小案例

上面那个案例是字符串类型的refs,虽然非常简单,但在react官方文档内,不推荐使用这种方式,会出现一些效率上的问题,甚至以后会废弃。所以上面那个案例我们只做refs的学习的入门。

所以现在我们来学习回调形式的ref。

        1.我们尝试把 ref='input1' 改为箭头函数类型:ref={(a)=>{console.log(a)}},此函数参数为a,再在函数体内打印a,看看a是什么东西。

        字符串形式:<input ref='input1' type="text" placeholder="点击按钮提示数据"/>

        函数形式:<input ref={(a)=>{console.log(a)}} type="text" placeholder="点击按钮提示数据"/>

打印结果:

        

 可见,参数a就是本dom节点,就是这个input框当作参数传入了ref。因为箭头函数并没有自己的this,它会把外层render函数的this作为自己的this,而reander函数的this指向组件构造的实例,所以本箭头函数就能和实例相关联,此ref所处的dom节点挂载到实例上并且取名为“input1”。

所以这个形参叫做a就不太合适了,适合叫做currentNode(当前节点),见名知意,或简略称为c。结合箭头函数的特性,只有一个参数,那么可以省略参数的小括号,函数体也就一句,也可以省略箭头函数右侧花括号。所以ref精简如下:

        ref={c => this.input1 = c}

2.事件函数会发生改变。原来是 const {input2} = this.refs ,而现在是 const {input2} = this ,因为此ref所处的dom节点挂载到实例上,所以直接写this即可。

代码如下:

 <script type="text/babel">
        //创建组件
        class Demo extends React.Component {
            showData = () => {
                const {input1} = this
                alert(input1.value)
            }
            showData2 = () => {
                const {input2} = this
                alert(input2.value)
            }
            render() {
                return (//构建虚拟dom
                    <div>
                        <input ref={c => this.input1 = c} type="text" placeholder="点击按钮提示数据" />&nbsp;
                        <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                        <input ref={c => this.input2 = c} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />&nbsp;
                    </div>
                )
            }

        }

        //渲染组件到界面
        ReactDOM.render(<Demo />, document.getElementById('test1'))
    </script>

2.2回调ref中调用次数问题

我们先上代码:

 class Demo extends React.Component {
            showInfo = ()=>{
                const {input1} = this
                console.log(input1);
            }
            render() {
                return (//构建虚拟dom
                    <div>
                        <input ref={c => this.input1 = c} type="text" />
                        <button onClick={this.showInfo}>点我提示输入的数据</button>
                    </div>
                )
            }

        }

        //渲染组件到界面
        ReactDOM.render(<Demo />, document.getElementById('test1'))

再引入react官网关于回调ref的说明:

        如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。

        注意,我们上面所写代码,将ref回调函数写在input标签内,这就是以内联函数的方式定义ref回调函数,符合官网的说明。但是,在更新过程中才会被执行两次,一次null一次传入参数dom元素,第一次执行render渲染dom是初始化,并非更新。

        所以我们在案例上面再加上数据更新的案例,让这个案例有更新状态,再来验证这个回调次数的问题。新加入的案例,就是我们之前写过的,炎热与凉爽切换的案例。另外在ref的回调函数内打印一下回调函数的参数c。代码如下:

//创建组件
        class Demo extends React.Component {
            state = { isHot: false }
            showInfo = () => {
                const { input1 } = this
                alert(input1.value)
            }
            changWeather = ()=>{
                const {isHot} = this.state
                this.setState({isHot:!isHot})
            }
            render() {
                const { isHot } = this.state
                return (//构建虚拟dom
                    <div>
                        <h2>今天天气很{isHot ? '炎热' : '凉爽'}</h2>
                        <input ref={c => {this.input1 = c;console.log('@',c)}} type="text" /><br/><br/>
                        <button onClick={this.showInfo}>点我提示输入的数据</button><br/><br/>
                        <button onClick={this.changWeather}>点我切换天气</button>
                    </div>
                )
            }

        }
        //渲染组件到界面
        ReactDOM.render(<Demo />, document.getElementById('test1'))

效果如下,刚打开页面:

 点击切换按钮切换页面显示文字后:

         这正好印证了官网所言。更新时,第一次回调函数参数为null,第二次才把dom节点当参数传进来。但这并不影响功能的正常。

解释:页面刚加载好的时候,是没有这个情况的,是因为调用render函数,在里面发现回调函数形式的ref,接着根据ref函数内所写把相应的dom节点当作参数传入。

        但是在更新的时候,state会发生改变,state会驱动页面显示,重新调用render函数。在重新调用render时,发现虚拟dom中回调函数式的ref,但react不确定之前调用这个ref的时候,接收的参数,函数执行发生了什么,所以直接先清空这个ref,传参为null,然后再调用这个ref传入当前dom节点参数。

        避免这种问题:官网所言:通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。

        那什么是class绑定函数的方式呢?我们来修改上面的代码:

原来的代码:

<input ref={c => {this.input1 = c;console.log('@',c)}} type="text" />

这样的将回调函数式ref写在dom节点内,我们称其为内联式ref。

将这段代码进行修改:

<input ref={this.saveInput} type="text" />

并且将saveInput函数放在实例自身上。

 saveInput = (c) => {
                this.input1 = c
                console.log("@", c);
            }

这样就变成了class的绑定函数方式,它不会影响功能的正常运行。这种情况,就不会发生更新后,它被调用两次,第一次是null的情况了。

3.createRef

3.1createRef的使用

        React.createRef是react的一个api,调用后可以返回一个容器,该容器可以存储被ref所标识的节点。所以我们使用它时,可以创建createRef容器赋值给myRef并且挂载到实例上:

                myRef = React.createRef()

        然后直接在render函数相应的标签内使用:

                <input ref={this.myRef} type="text" />

        这样就相当于把input这个dom节点当作ref的参数,将这个ref放进myRef这个容器里面。

来看代码:

 //创建组件
        class Demo extends React.Component {
            /*
                React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点
            */
            myRef = React.createRef()//创建容器赋值给myRef并且挂载到实例上
            showInfo = () => {
                console.log(this.myRef);
            }
            render() {
                return (//构建虚拟dom
                    <div>
                        <input ref={this.myRef} type="text" />
                        <button onClick={this.showInfo}>点我提示输入的数据</button>
                    </div>
                )
            }
        }
        //渲染组件到界面
        ReactDOM.render(<Demo />, document.getElementById('test1'))

我们打印this.myRef看看输出结果:如图所示,打印出的是一个对象,里面的current是我们放入容器的input节点。

 然后打印this.myRef.current:如图所示,打印出了input节点。

 最后打印this.myRef.current.value:如图所示打印出了input框内的值。

 

 3.2createRef的“专人专用”

注意,虽然createRef作为一个容器,但它是“专人专用”的,只能往一个容器内放置一个。我们可以尝试写一个案例,往createRef容器里面塞两个ref。

上面的那个案例,我们给button也设置上createRef容器:

  <input ref={this.myRef} type="text" />
  <button ref={this.myRef} onClick={this.showInfo}>点我提示输入的数据</button>

并且打印输出,console.log(this.myRef);

结果如下:

 显而易见,createRef内只能存在一个ref,后面的会顶替掉前面的。

        所以我们得多创建几个createRef,保证每个“人”都有createRef用。我们创建两个createRef,分别赋值给myRef和myRef2。为两个input分别分配myRef和myRef2,在showInfo和showInfo2内对两个容器做不同的处理。

代码如下:

//创建组件
        class Demo extends React.Component {
            /*
                React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点
            */
            myRef = React.createRef()//创建容器赋值给myRef并且挂载到实例上
            myRef2 = React.createRef()
            showInfo = () => {
                alert(this.myRef.current.value);
                console.log(this.myRef);
            }
            showInfo2 = () => {
                alert(this.myRef2.current.value)
            }
            render() {
                return (//构建虚拟dom
                    <div>
                        <input ref={this.myRef} type="text" placeholder="点击按钮提示内容" />
                        <button onClick={this.showInfo}>点我提示输入的数据</button>
                        <input ref={this.myRef2} onBlur={this.showInfo2} type="text" placeholder="失去焦点提示内容" />
                    </div>
                )
            }
        }
        //渲染组件到界面
        ReactDOM.render(<Demo />, document.getElementById('test1'))

这样在需要多个createRef容器的情况下,就可以正常的使用。但也有弊端,就是reander函数内用几个createRef,上面实例内就需要创建几个createRef。但这个createRef还是react官方最推荐的ref使用方式。

4.ref总结

在学完三种ref的使用方式后,我们总结一下:

1.尽量避免使用字符串形式的ref,但尽管写了字符串形式的ref,也不会有太大问题。

2.回调形式的ref稍微麻烦一些。不要太纠结回调函数的回调执行次数问题,内联式的回调函数可以写,官网说了:将 ref 的回调函数定义成 class 的绑定函数的方式,但大多数情况无关紧要。

3.createRef是最麻烦的,但目前来说是react官方最推荐的。

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

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

相关文章

SpringBoot 插件 spring-boot-maven-plugin 原理,以及SpringBoo工程部署的 jar 包瘦身实战

spring-boot-maven-plugin 我们直接使用 maven package &#xff08;maven自带的package打包功能&#xff09;&#xff0c;打包Jar包的时候&#xff0c;不会将该项目所依赖的Jar包一起打进去&#xff0c;在使用java -jar命令启动项目时会报错&#xff0c;项目无法正常启动。这…

TOP RPA·脱普×实在丨日用品企业脱普签约实在智能,构建全域数据智能运营系统

近日&#xff0c;实在智能与脱普日用化学品&#xff08;中国&#xff09;有限公司&#xff08;简称“脱普企业”&#xff09;在脱普企业上海总部举行“全域数据智能运营”项目启动会&#xff0c;双方领导及项目组关键成员共同参会&#xff0c;就项目目标、实施进程、沟通机制等…

Spring Boot中使用Spring Data Elasticsearch访问Elasticsearch

Spring Boot中使用Spring Data Elasticsearch访问Elasticsearch Elasticsearch是一个分布式的全文搜索和分析引擎&#xff0c;它可以将海量数据进行快速的查询和聚合。Spring Data Elasticsearch是Spring Data家族中的一个成员&#xff0c;它提供了与Elasticsearch的集成&…

一起来学习怎样识别表格文件吧

你有没有经历过手头有一堆纸质表格&#xff0c;但是又不想手动输入数据的烦恼&#xff1f;现在&#xff0c;表格识别计数的出现&#xff0c;可以帮助你轻松解决这个问题。它通过拍照扫描&#xff0c;来自动提取表格中的信息&#xff0c;并将其转化为可编辑的电子文档。那么&…

c# 动态表达式

准备&#xff1a; 创建一个空项目&#xff0c;nuget查找并安装ExpressionEvaluator 示例&#xff1a; using ExpressionEvaluator; using System; 一、计算简单表达式 public string Test1() { return SimpleEval("0.1*(Math.Pow(10,2)20)"); …

AI小作文搞崩科大讯飞股价 科技“魔法”反噬科企

5月24日午后&#xff0c;A股公司科大讯飞的股价突然走出深V造型&#xff0c;闪崩8%。科大讯飞回应称&#xff0c;股价下跌系某生成式AI写作虚假小作文导致&#xff0c;谣传风险为不实消息。 网传的一篇“小作文”谣称“科大讯飞被曝采集用户隐私数据研究人工智能引发争议”&am…

Windows下编写的shell脚本无法在Linux上执行

这通常是由于回车换行符不兼容导致的。 出现无法执行&#xff0c;提示诸如“ 未预期的符号“$\r”附近有语法错误”&#xff0c;“syntax error near unexpected token in”之类的错误&#xff0c;可尝试此文方法。 1.查看shell脚本的换行符格式 vi/vim进入文件&#xff0c;…

2023年湖北建筑起重信号司索工报名流程是什么?个人可以报名吗?

2023年湖北建筑起重信号司索工报名流程是什么&#xff1f;个人可以报名吗&#xff1f; 建筑起重信号司索工是特种作业人员工种即是建设厅特种工。证书全国通用&#xff0c;两年需要年审一次&#xff0c;六年需要换一次证。报考有一定的条件和要求。搜一下启程别就知道啦。 湖北…

【Leetcode】697. 数组的度

[哈希表] Given a non-empty array of non-negative integers nums, the degree of this array is defined as the maximum frequency of any one of its elements. Your task is to find the smallest possible length of a (contiguous) subarray of nums, that has the sa…

20230525下载youtube的字幕的方法

20230525下载youtube的字幕的方法 百度&#xff1a;youtube 字幕 (英语自动生成)下载 【可以直接下载字幕&#xff01;】 https://zhuanlan.zhihu.com/p/349506890?ivk_sa1025883i 下载YouTube油管字幕的2种方法 二&#xff0e;使用在线网站下载YouTube字幕文件 二&#xff0e…

【剧前爆米花--爪哇岛寻宝】TCP保证效率,应对特殊情况等相关机制

作者&#xff1a;困了电视剧 专栏&#xff1a;《JavaEE初阶》 文章分布&#xff1a;这是一篇关于网络编程的文章&#xff0c;在这篇文章中我会着重介绍TCP保证效率&#xff0c;应对特殊情况等相关机制&#xff0c;希望对你有所帮助&#xff01; 目录 效率 批量传输 滑动窗口 …

【九章刷题录】C/C++:数组中重复的数字(JZ3)

精品题解 &#x1f449; 九章刷题录&#x1f448; 猛戳订阅 JZ3 - 数组中重复的数字 &#x1f4dc; 目录&#xff1a; 「 法一 」暴力大法&#xff08;BF&#xff09; 「 法二 」排序 遍历 「 法三 」哈希集合 「 法四 」哈希无序集 「 法五 」原地哈希 「 法六 」Map …

北京筑龙作为软件服务商出席《国企阳光采购标准》研讨会

近日&#xff0c;由中国企业国有产权交易机构协会市场创新专业委员会主办、青岛产权交易所有限公司承办的《国企阳光采购标准》研讨会在青岛召开&#xff0c;该会议共有19家已开展采购业务的各地产权交易机构参加&#xff0c;北京筑龙作为软件服务商出席会议。 《国企阳光采购标…

【Linux】线程同步

文章目录 条件变量相关函数初始化条件变量-pthread_cond_init销毁条件变量-pthread_cond_destroy等待条件变量-pthread_cond_wait唤醒等待条件变量pthread_cond_broadcastpthread_cond_signal 小例子关于等待函数的补充条件变量使用规范 条件变量相关函数 初始化条件变量-pthr…

多台linux设备之间设置免密登陆

1、首先&#xff0c;先搞一个公钥&#xff0c;如果已经有公钥了&#xff0c;请自行跳过 我这是有了&#xff0c;如果没有&#xff0c;也没关系&#xff0c;造一个就好&#xff0c;命令为&#xff1a; ssh-keygen -t rsa 一路回车就行&#xff0c;看下执行结果&#xff1a; 2、…

使用AWTK实现汽车仪表Cluster/DashBoard嵌入式开发概述

AWTK=Toolkit AnyWhere,一款国产免费开源工具,ZLG开发的开源GUI引擎,为嵌入式等系统提供图形界面开发IDE。 随着汽车技术的发展,汽车仪表盘也在快速发展,从最初的机械式到电气式,再到数字化。这次电动化、智能化又一次让汽车仪表出现了飞跃式的发展,再未来,仪表板上可…

【vsomeip】vsomeip安装与入门案例

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍vsomeip的使用。 学其所用&#xff0c;用其所学。——梁启超 欢迎来到我的博客&#xff0c;一起学习知识&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下次更新不迷路&am…

luatOS网站 lua语言学习 练习题

lua 教程跳转链接&#xff0c;练习题都来自这里 逻辑运算 检验大小&#xff08;自测题&#xff09; 题目&#xff1a;如果已知number变量n&#xff0c;那么如果需要判断n是否符合下面的条件&#xff1a; 3<n≤10 以下四行判断代码&#xff0c;正确的是&#xff1f; &a…

【C++】运算符重载(日期类的实现)

【C】运算符重载&#xff08;日期类的实现&#xff09; 前言运算符重载operator全局和类中 日期类的实现成员变量的确定构造函数拷贝构造 运算符重载部分的重载思路实现GETmonthdayoperator 的重载思路实现 -的与-的重载实现 各个比较运算符的重载实现 前置与后置实现 &#xf…

接口自动化【六】——接口关联之jsonpath提取+设置全局变量+通用封装

文章目录 前言一、jsonpath提取二、jsonpath与excel当中提取表达式结合三、类的动态属性设置四、设置全局变量&#xff08;这个模块就作为一个讲解&#xff09;五、new_handle_global_data.py六、new_handle_extract.py七、test_new_upload_image.py 文件中上传图片的代码更改八…