使用refs获取节点_闲庭信步聊前端 - 原来你是这样的Refs

28cf291fcad90a348df1884c542c5d23.png

7f326d720ddbec59b8e26103a3ae3f97.png

一、refs 的由来

什么是refs

refs是拿到真实的DOM节点和React元素实例的一种方法。在React官方文档中有提到Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。 React是单向的数据流,父子组件的交互是通过props。修改子组件需要使用新的props来重新渲染子组件。但是在某些情况下,你需要修改数据流以外的子组件,如DOM元素或者一个React元素实例,此时就需要Refs来改变。

refs的适用场景

根据官方文档,refs在以下几种场景中适用: 1. 操作DOM元素、控制文本内容或者媒体播放 2. 操作DOM元素,触发强制动画 3. 集成第三方DOM库

我们来看下,React中有以下四种使用方式

二、refs的四种方式

1. callback ref

React 支持一种通过回调的方式设置refs,这种方式可以控制refs何时被设置和解除。创建ref属性时,你会传递一个函数,这个函数会接受DOM元素或者组件实例作为参数,使他们能够在其他地方被访问。

class TestTemp extends React.Component {componentDidMount() {this.myRef.focus();}render() {return <input ref={(element) => {this.myRef = element;}} />;}
}

React在componetDidMount或者componetDidUpdate触发前调用ref回调,并且传入对应的DOM元素,保证refs是最新的。 在下面的例子中,父组件可以获取到子组件的DOM节点

function Child(props) {return (<div><input ref={props.myRef} /></div>);
}
class Parent extends React.Component {componentDidMount() {this.myElement.focus();}render() {return (<ChildmyRef={element => this.myElement = element}/>);}
}

父组件通过props传一个回调函数给子组件,子组件可以把这个函数作为ref属性的值绑定到DOM元素上。this.myElement就是对应子组件的DOM元素(input)

2. React.createRef()

createRef()是通过创建一个ref属性,传递给渲染的DOM元素或者是组件实例

class TestTemp extends React.Component {constructor(props) {super(props);this.myRef = React.createRef();}componentDidMount() {this.myRef.current.focus()}render() {return (<input ref={ this.myRef }></input>)}
}

在上面的例子中,创建一个ref属性实例myRef,并将其传给DOM元素 input中。之后对此元素的操作就可以在ref实例的current属性中处理。 对元素的操作根据节点类型的不同,ref的值也不同: 1. 当ref属性作用于一个HTML元素时,createRef()创建ref实例的current实例是底层DOM元素

class TestTemp extends React.Component {constructor(props) {super(props);// 创建一个ref存储 input元素this.myRef = React.createRef();}componentDidMount() {// 获取 input元素焦点this.myRef.current.focus()}render() {return (<div><input ref={this.myRef}></input></div>)}
}

2. 当ref属性作用于一个自定义组件时,createRef()创建ref实例的current实例是组件的挂载实例

class Children extends React.Component {constructor(props) {super(props);// 创建一个ref实例,存储input元素this.childRef = React.createRef();}childInput() {// 获取input焦点this.childRef.current.focus();}render(){return(<div><input type='text' ref={ this.childRef }/></div>)}
}
class Parent extends React.Component {constructor(props) {super(props);// 创建ref实例 存储Children 组件实例this.parentRef = React.createRef();}componentDidMount() {// 通过ref调用Children 组件的childInput方法,获取子组件input的焦点this.parentRef.current.childInput();}render() {return (<Children ref={ this.parentRef } />);}
}

3. ref属性不可在函数组件上使用,因为函数组件没有实例。

3. useRef()

useRef 是hooks家族中的一员,他在react hook中的作用,像是一个变量,类似如this,它可以存放任何的东西。createRef每次都会创建一个新的实例,而useRef每次都会返回相同的引用,返回的ref对象在组件的整个生命周期内保持不变。 因为特性的不同,createRef和useRef也有很大的不同

function TestTemp() {const [renderIndex, setRenderIndex] = useState(1);const refFromUseRef = useRef();const refFromCreateRef = createRef();if (!refFromUseRef.current) {refFromUseRef.current = renderIndex;}if (!refFromCreateRef.current) {refFromCreateRef.current = renderIndex;}return (<div>index: {renderIndex}<br />在refFromUseRef:{refFromUseRef.current}<br />在refFromCreateRef:{refFromCreateRef.current}<br /><button onClick={() => setRenderIndex(prev => prev + 1)}>增加</button></div>);
}

执行上面的代码,会发现index和refFromCreateRef是一直在随着点击增加的,而refFromUseRef则是保持不变。 就算组件重新渲染,由于refFromUseRef的值一直存在,类似于this,无法重新赋值,因此结果不会变。当ref对象内容发生变化时,useRef并不会通知你,更改current属性也不会导致重新渲染,因为它一直时一个引用。

何时适合用useRef

那么为什么要有useRef这个API呢,我们来看一下下面的例子

function TestTemp() {const [count, setCount] = useState(0);function handleAlertclick() {setTimeout(() => {alert("count:" + count);}, 2000);}return (<div><p>当前count: {count} </p><button onClick={() => setCount(count + 1)}>增加</button><button onClick={handleAlertclick}> 弹窗</button></div>)
}

按照如下步骤执行操作: 1. 点击两次增加按钮后,点击弹窗按钮 2. 在弹窗未展示之前,迅速再次点击两次增加按钮,等待弹窗出现

执行操作后发现,弹窗显示的结果是"count: 2",页面上的count值为4。弹窗显示的只是当时点击时的快照。

为什么弹窗中不是最新的count呢?

当我们更新状态时,React会重新渲染组件,每一次渲染都会拿到当前的count值,并重新渲染点击事件(handleAlertclick函数),因此每个函数里面都有自己的count。这样我们就理解了上面的例子中,弹窗中就是点击时的count值。

如何弹出时获取到实时的值呢?

function TestTemp() {const [count, setCount] = useState(0);const useRefCount = useRef(count);useEffect(() => {useRefCount.current = count;});function handleAlertclick() {setTimeout(() => {alert("useRefCount.current:" + useRefCount.current + "count:" + count);}, 2000);}return (<div><p>当前count: {count} </p><button onClick={() => setCount(count + 1)}>增加</button><button onClick={handleAlertclick}>弹窗</button></div>)
}

执行同上个例子中的操作,结果弹窗中显示"useRefCount.current: 4 count: 2"。使用 useRef 能获取到最新的值,但是 useState 却不能。 因为useRef每次都会返回同一个引用,因此在useEffect中修改时,弹窗中的也会被修改。

4. 过时API:string ref

class TestTemp extends React.Component {componentDidMount() {this.refs.myRef.focus();}render() {return <input ref="myRef" />;}
}

string ref是通过this.refs.myRef来访问DOM节点。但是现在不建议以这种方式创建ref属性,它已过时并可能在未来的版本被移除。React官方文档中有提出

如果你目前还在使用 this.refs.textInput 这种方式访问 refs ,我们建议用回调函数或 createRef API 的方式代替。

826c8b9df74ae613c8706d494c36c441.png

三、Refs转发

通常我们不需要在父组件中引用子组件中的DOM节点,但是在一些特殊情况下,避免不了这种需求,比如一些可重用的组件。因此我们可以使用ref转发,ref转发使组件可以像暴露自己的ref一样暴露子组件的ref。

Ref 转发是一个可选特性,其允许某些组件接收 ref,并将其向下传递(换句话说,“转发”它)给子组件。 如果你使用16.3或者更高版本的React可以使用React.forwardRef来获取传递的ref,并且传递给需要引用的DOM元素
// React.forwardRef()接收ref作为第二个参数
// 子组件接收此ref并且传递给DOM元素
const Child = React.forwardRef((props, ref) => (<button ref={ref}>{props.children}</button>
));
// 父组件创建一个ref传递给子组件
// 通过myRef.current就可以引用button元素
const myRef = React.createRef();
<Child ref={myRef}>点击</Child>

如果你使用的是低版本的React,可以通过props传递ref来模拟React.forwardRef()函数

function Child(props) {return (<div><input ref={props.childRef} /></div>);
}
class Parent extends React.Component {constructor(props) {super(props);this.myRef = React.createRef();}componentDidMount() {this.myRef.current.focus();}render() {return (<Child childRef={this.myRef} />);}
}
注意:ref只在React.forwardRef()函数中作为第二参数存在,在常规的函数和Class组件是不接收ref参数的

四、Refs 传递原理

React中,HostComponent、ClassComponent、ForwardRef可以赋值ref属性。ForwardRef只是将ref作为第二个参数传递下去,没有别的特殊处理。 Ref属性在ref不同的生命周期会被执行 ( fuction类型 ) 或赋值 ( {current: any}对象类型 ) ref的生命周期与react的渲染一样,可以分为两个阶段

render阶段:

为含有ref属性的component对应fiber添加Ref effectTag, fiber类型为HostComponent、ClassComponent、ScopeComponent

commit阶段:

当给HTML元素添加ref属性时,ref回调接收了底层的DOM元素作为参数。在源码中有以下两个方法 1. commitAttachRef 挂载实例,当组件渲染完成后,在componentDidMount/componentDidUpdate后被执行。finishedWork为含有Ref effectTag的fiber 挂载时将DOM元素传入ref的回调函数

e9efa6b70990393b94d74658b852c0da.png

2. commitDetachRef 移除实例,在组件或元素被销毁前执行,在componentWillUnmount之前清理引用。 卸载时传入null,将当前的DOM元素赋值为null

52d244e2d645dc43989b859f10357444.png

5657566c3c4e352c676a34133e43f187.png

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

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

相关文章

从TimeSpan说起

小编在编写WPF程序时&#xff0c;需要做一个判断&#xff1a;定时使用Modbus协议使用Quartz.net 定时任务读取设备中的数据&#xff0c;同时也使用定时任务判断是否长时间获取不到数据的情况&#xff0c;如果程序中超过一分钟没有获取到数据&#xff08;数据没有更新&#xff0…

如何维持手机电池寿命_延长手机电池寿命终极技巧教学,iPhone和安卓手机皆适合...

每隔一阵子就开始有不少用户询问&#xff0c;iPhone电池健康度又剩下多少了&#xff0c;怎么朋友的还维持在100%&#xff0c;是不是我手机电池出问题&#xff0c;引起不少用户翻白眼。会有电池健康度&#xff0c;最主要是要让大家了解目前手机电池当前寿命&#xff0c;只要电池…

2020年了,再不会Https就老了

合格的web后端程序员&#xff0c;除搬砖技能&#xff0c;还必须会给各种web服务器启用Https&#xff0c;本文结合ASP.NET Core部署模型聊一聊启用Https的方式。温故知新目前常见的Http请求明文传输&#xff0c;请求可能被篡改&#xff0c;访问的站点可能被伪造。HTTPS是HTTP加上…

c语言枚举类型例题_[开源资讯]Zig 0.6.0 发布,想要挑战 C 语言

Zig 0.6.0 已发布&#xff0c;这是一门通用编程语言&#xff0c;专为稳定性、可维护性和性能而设计&#xff0c;追求替代 C 语言在系统编程上的最佳地位。Zig 具有以下值得关注的特性&#xff1a;手动管理内存与 C 语言竞争而非依赖它&#xff0c;Zig 标准库不依赖于 libc轻量而…

【实战 Ids4】║ 给授权服务器加个锁——HTTPS配置

在上篇文章《【实战 Ids4】║客户端、服务端、授权中心全线打通&#xff01;》中&#xff0c;我们正式的将三站打通&#xff0c;发布过后&#xff0c;有小伙伴反馈&#xff0c;可能Nginx对配置HTTPS安全协议有点儿问题&#xff0c;我也就半夜趁着没人打扰&#xff0c;疯狂的研究…

快速幂 a*b%c

2020.12.30开始学习AcWing算法《算法竞赛进阶指南》&#xff1b; 在CSDN上传博客方便复习。 //wecccccccc //2020.12.30 #include <iostream> using namespace std; typedef long long int ll;ll fast_power(ll a, ll b, ll c) {ll ans 1;a % c;//防止一开始输入的值过…

thinkphp 助手函数url不生成https_如何用ThinkPHP框架写一个快递查询接口

ThinkPHP是php程序员们经常使用的框架之一&#xff0c;运用框架来开发网站无疑减轻了我们代码量&#xff0c;加快了我们的开发速度&#xff0c;框架底层封装的方法和函数使用起来简直不能太爽。今天给大家总结一个小案例&#xff0c;如何实现一个快递查询的接口。所谓接口&…

64位整数乘法

2020.12.30开始学习AcWing算法《算法竞赛进阶指南》&#xff1b; 上传博客方便复习。 //Wecccccccc //2020.12.30 #include <iostream> using namespace std; typedef unsigned long long int ull;int main() {ull a, b, c;cin >> a >> b >> c;ull ans…

分布式锁的实现与探索

源宝导读&#xff1a;大型的信息化系统对数据准确性的要求很高&#xff0c;所以经常会使用事务、锁、队列等技术&#xff0c;保障高并发下的数据一致性问题。本文将讨论在分布式部署模式下&#xff0c;如何利用锁机制保证业务数据准确的技术探索与实践。一、背景分布式场景下的…

matlab 角度转四元数_基于Matlab的机械臂路径规划

什么是 trajectory(路径)规划中文路径在英语中可能有两种翻译&#xff1a;1. path2. trajectory首先告诉大家&#xff0c;我们所说的“路径”是后者——trajectory。我们看一下这两种“路径”在机械臂的世界里有什么区别。设想机械臂的 end-effector 要从 A 点运动到 B 点&…

ASP.NET Core 中间件的几种实现方式

前言ASP.NET Core 中 HTTP 管道使用中间件组合处理的方式,换句人话来说,对于写代码的人而言,一切皆中间件.业务逻辑/数据访问/等等一切都需要以中间件的方式来呈现.那么我们必须学会如何实现自定义中间件 这里划重点,必考这里我们介绍下中间件的几种实现方式...匿名函数通常新建…

cookies默认过期时间_「图」Chrome Canary新版已启动“增强版cookies控制”预览测试...

近日谷歌承诺将于今年晚些时候在Chrome浏览器启动“增强版cookies控制”的预览测试。在今年的I/O开发者大会上&#xff0c;谷歌宣布携手Mozilla等开发者耗时3年多时间制定了名为“same-site cookies”的IETF 标准&#xff0c;而该功能就是建立在same-site cookies的基础上&…

【原创】StackOverflow 20万关注的问题:如何实现异步Task超时的处理?

前文传送门 dotNET开发系列收藏&#xff01;推荐12个超实用的Visual Studio插件程序员&#xff1a;这10种糟糕的程序命名&#xff0c;你遇到过几个&#xff1f;使用Vistual Studio N年&#xff0c;推荐2个异常捕获的技巧面试官&#xff1a;你连RESTful都不知道我怎么敢要你&…

C# WPF 表单更改提示

微信公众号&#xff1a;Dotnet9&#xff0c;网站&#xff1a;Dotnet9&#xff0c;问题或建议&#xff0c;请网站留言&#xff1b; 如果您觉得Dotnet9对您有帮助&#xff0c;欢迎赞赏Dotnet9.com内容目录实现效果业务场景编码实现本文参考源码下载1.实现效果未做修改的表单展示 …

XRPC接口双向调用

一般远程接口调用的服务都是基于客户端主动调用服务端&#xff0c;由服务端来提供相关的接口服务&#xff1b;在新版本的XRPC中引入了一个新的功能&#xff0c;即接口双向通讯&#xff0c;组件提供服务创建客户会话的接口代理并调用客户提供的接口服务。接下来介绍如何通过XRPC…

电机控制pid_微电机控制如此简单,揭秘微电机调速的控制,PID控制之双环调速...

​​在微型电机应用中&#xff0c;由于各种应用产品的不同会用到不同的调速方式&#xff0c;在调速中可能会遇到各种不同的问题&#xff0c;下面天孚电机对最近客户对微电机PID调速遇到的双环控制问题来讲一讲。智能小车TFMOTOR-N30减速电机​客户是做智能小车的采用的是tfn30微…

简单的01背包和完全背包

2020.12.30开始学习AcWing算法《算法竞赛进阶指南》&#xff1b; 上传博客方便复习。 01背包&#xff08;每种物品只能使用一次&#xff09;&#xff1a; //Wecccccccc //2020.12.31 #include <iostream> using namespace std; int n,m,v[1010],w[1010],dp[1010]; int…

图片上传组件_配置Django-TinyMCE组件 实现上传图片功能

Django自带的Admin后台&#xff0c;好用&#xff0c;TinyMCE作为富文本编辑器&#xff0c;也蛮好用的&#xff0c;这两者结合起来在做博客的时候很方便&#xff08;当然博客可能更适合用Markdown来写&#xff09;&#xff0c;但是Django-TinyMCE这个组件默认没有图片上传功能的…

.NET Core 3.1之深入源码理解HealthCheck(二)

写在前面前文讨论了HealthCheck的理论部分&#xff0c;本文将讨论有关HealthCheck的应用内容。可以监视内存、磁盘和其他物理服务器资源的使用情况来了解是否处于正常状态。运行状况检查可以测试应用的依赖项&#xff08;如数据库和外部服务终结点&#xff09;以确认是否可用和…

多重背包问题以及二进制优化

2020.12.30开始学习AcWing算法《算法竞赛进阶指南》&#xff1b; 上传博客方便复习。 多重背包问题&#xff08;N< 100): //Wecccccccc //2020.12.31 #include <iostream> using namespace std; int n,m,dp[110]; int main() {cin>>n>>m;for(int i0;i&…