React ref的基本使用
React出现后,提供了state, props,前端开发者无须在直接操作dom。React官方不推荐我们直接访问、操作DOM,但是,还是为我们留了一个后门ref,方便访问操作DOM。因为在某些特定的场景,必须使用ref来访问DOM元素。比如:input的focus,媒体播放器、组件的位置,动画,引入第三dom方库。
创建和获取ref
在reactd的版本历史上,出现了三种创建ref的方式:string ref,callback ref,React.createRef。无论哪种方式,都是为ref属性赋值,ref和key一样,都是关键字,为React内部使用。 另外,值得注意的是,所有的ref获取,最好在组件加载结束之后,否则无法获取值。因为组件加载后,dom才准备好了是吧。
string ref
class Test extends React.Component {componentDidMount(){// 获取console.log(this.refs.first);// <input value="first">}render() {// 创建return <input value="first" ref="first" />}
}
复制代码
string ref创建的ref的方法在React16.3之后的版本弃用了,并且官方表示,在16.3之前,尽量使用callback ref来创建ref。因为string ref创建的ref带有些问题,具体原因见连接。
callback ref
class Test extends React.Component {componentDidMount(){// 获取console.log(this.second);// <input value="second">}render() {// 创建return <input value="second" ref={(input) => {this.second = input }} />}
}
复制代码
通过回调函数的方式创建ref,形势上看上去稍微有些繁琐。并且,callback以上面这种内联的方式赋值,在组件发生了更新时,ref都会重新创建。可通过将这个回调函数变成类的方法来避免。
class Test extends React.Component {componentDidMount(){// 获取console.log(this.second);// <input value="second">}createRef = (dom) => {this.second = dom;}render() {// 创建return <input value="second" ref={this.createRef} />}
}
复制代码
React.createRef
class Test extends React.Component {constructor(props) {super(props);// 创建this.third = React.createRef();}componentDidMount(){// 获取console.log(this.third.current);// <input value="third">}render() {// 赋值return <input value="third" ref={this.third} />}
}
复制代码
React.ref是React16.3后新加的一个创建ref的方法,写法相对于callback ref的写法相对简洁。将创建的ref赋值给不同的子元素,ref的current的值有所区别。
不同子元素的ref值
这里讨论通过callback ref和React.createRef创建的ref,赋值给不同的子元素后,ref的取值的不同。
HTML元素
为HTML元素的ref赋值,获取到的ref的值为这个HTML元素对应的DOM元素。
class Test extends React.Component {second = React.creteRef()handleSubmit = () => {console.log(this.first);// <input value="first">console.log(this.second.current);// <input value="second">}render() {// 创建return (<div><input value="first" ref={(input) => {this.first = input} } /><input value="second" ref={this.second} /><button onClick={this.handleSubmit} >提交</button></div>)}
}
复制代码
class类创建的React组件
为class类创建的React组件的ref赋值后,最终获取到的值为这个React组件的实例。
class Test extends React.Component {second = React.createRef()handleSubmit = () => {console.log(this.first);// Hello {props: {…}, context: {…}, refs: {…}, updater: {…}, _reactInternalFiber: FiberNode, …}console.log(this.second.current);// Hello {props: {…}, context: {…}, refs: {…}, updater: {…}, _reactInternalFiber: FiberNode, …}}render() {return (<div><Hello ref={(input) => {this.first = input}} /><Hello ref={this.second} /><button onClick={this.handleSubmit}>submit</button></div>)}
}
复制代码
function创建的React组件
遗憾的是,两种方法均无法为function创建的React组件ref赋值,就算赋值,获取到的最终结果为null。
进阶的ref使用
通过react提供的ref,父组件可以获取到具体的某个子元素,这是基本的用法。以下还将提到一些进阶用法。
React.forwardRef
这里有个问题存在,有没有办法穿过父组件,获取子元素?正好,与React.createRef一起出世的还有一个用于解决这个问题的直接办法React.forwarRef。其实,这个这个问题主要出现在HOC高阶组件上,开发者可以通过React.forwardRef获取WrapperedComponnet,而不是外面的包裹层。
class FancyInput extends React.Component{render() {return <input value="fancyInput" />}
}const HOCFn = (WrapperedComponent) => {class Test extends React.Component {render() {const { forwardRef, ...rest } = this.props;return (<WrapperedComponent ref={forwardRef} {...rest} />)}}return React.forwardRef((props, ref) => {return <Test forwardRef={ref} {...props} />});
}const HocEdComp = HOCFn(FancyInput);export default class NewComp extends React.Component {handleSubmit = () => {console.log(this.testRef);// FancyInput}render() {return (<div><HocEdComp ref={(dom) => { this.testRef = dom }} /><button onClick={this.handleSubmit}>提交</button></div>)}
};
复制代码
最终,从上面的例子可知,this.testRef指向的是FancyInput,而非HocEdComp。
获取资源子元素中指定的DOM
父元素可以通过ref机制获取html元素,React组件实例,当然这两者都是作为父元素的子元素存在。在这里我们看到几点局限性:
- 无法获取React组件中的某个dom元素
- 无法获取function创建的组件
在这里,可以使用一些小技巧。看例子:
const First = (props) => {return <input value="first" ref={props.firstRef} />
}class Second extends React.Component {render() {return <input value="second" ref={this.props.secondRef} />;}
}export default class Test extends React.Component {handleSubmit = () => {console.log(this.first);// <input value="first">console.log(this.second)// <input value="second">}render() {return (<div><First firstRef={(input) => { this.first = input }} /><Second secondRef={(input) => { this.second = input }} /><button onClick={this.handleSubmit}>submit</button></div>)}
}
复制代码
这种方法的本质是,将创建的ref作为props传递给React组件,React组件的某个HTML元素的ref接收这个属性,父元素便可获取到子组件中具体的某一个HTML元素的底层DOM。这样做,破坏了组件的封装性,但是有时万不得已只能这么做了。
总结
React提供ref的初衷是给开发者一个可获取实际DOM的工具,目前,我们也可以通过ref获取子组件(React组件),但是,React还是提倡不要过度使用ref,,毕竟有了state,props,我们已经从各种繁杂的DOM操作中解放出来。在工作中,有时候设计到动画,或者媒体播放器,input聚焦时,我会使用以下ref,确实解决了state和props无法解决的问题。
我只是总结可以如何使用ref,并没有细细了解ref的原理,找个机会希望可以一探究竟,又当了一次搬运工。
参考资料
Refs and the DOM
Forwarding Refs
转载于:https://juejin.im/post/5c26137d5188257dc54af75c
React ref的基本使用相关推荐
- 一文搞懂 React ref
最近也开始研究React,这篇文章主要是讲述 Ref 相关的内容,如有错误请指正. ref 的由来 在典型的 React 数据流中,props 是父组件与子组件交互的唯一方式.要修改一个子组件,你需要 ...
- React ref用法
React ref 官方文档 1.介绍 Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素. DOM 节点:就是平常所说的能在页面中直接获取到的原生 ...
- React ref的转发
在前面学习ref时讲过,ref不能应用于函数式组件: 因为函数式组件没有实例,所以不能获取到对应的组件对象 但是,在开发中我们可能想要获取函数式组件中某个元素的DOM,这个时候我们应该如何操作呢? 方 ...
- React ref useRef 完全指南
在这篇文章中,你将学习如何使用React.useRef()钩子来创建持久的可变值(也称为references或refs),以及访问DOM元素. 我们将从下面几点讲解: 1. 可变值 - 1.1用例:记 ...
- ReactJS学习系列课程(React ref的使用)
在我学习React的过程当中,总会被一些名词搞得晕头转向,但是细想起来又非常简单,比如React定义的这个refs,其实就是用于获取dom的一种方式. 在React中组件并不是真实的 DOM 节点,而 ...
- react ref无法获取被高阶组件包装的原始组件问题
问题描述: react无法通过ref获取被高阶组件包装的原始组件 通过ref调用被dva connect包裹的组件报错 我们在平时使用ref获取一个组件的引用后,就可以直接通过ref调用组件自身的函数 ...
- React Ref 的使用
React 提供了 refrefref 属性,让我们可以引用组件的实例或者原生 DOM 元素.使用 refrefref,可以在父组件中调用子组件暴露出来的方法,或者调用原生 element 的 API ...
- 3.0 react ref 使用 读取子组数据 转发forword
1 定义 实例 类中: this.r1 = React.createRef();//实例一个ref , 包含current属性渲染完以后可以用 this.xx.current 来获取dom 回调 类= ...
- (精华)2020年7月26日 React ref的三种方式
import React from 'react'export default class RefDemo extends React.Component {constructor() {super( ...
最新文章
- 力扣(LeetCode)刷题,简单题(第6期)
- 互联网的中层管理,一个庞大且易脆的群体
- 在隐私的博弈时代,BCH为你保驾护航
- android工程师 腾讯,腾讯音乐Android工程师一面面试题记录,拿走不谢!
- 【错误记录】Android NDK 错误排查记录 ( error: undefined reference to | Linking CXX shared library FAILED )
- Linux驱动小技巧 | 利用DRIVER_ATTR实现调用内核函数
- GTK的.NET的函数库 GTK#
- Velox将在Pangolin上启动其算法交易机器人,并计划推出更多DeFi解决方案
- 郑州大学远程教育c语言程序设计答案,郑州大学远程教育C语言考试试卷.doc
- mysql 备份工具简介
- poj 1325 Machine Schedule 匈牙利二分匹配 基础
- Interesting Finds: 2008.01.04
- 事业单位资产管理系统破解资产管理难题,实现账、卡、物、地、人相符
- android dns 设置,安卓手机怎么设置DNS Android手机修改DNS图文教程
- web网站添加ico图标
- HandBrake视频压缩工具
- ntpdate离线安装
- 人人可以是好厨子:72招做饭技巧帮你变大厨
- 学生喂养三种宠物:猫、狗和鸟
- 肺结节圆形边界光滑_肺结节不是肺癌,千万别恐慌
热门文章
- windows10上运行magic keyboard和magic mouse
- Win7 TAP-Windows Adapter V9提示Windows 要求已数字签名的驱动程序
- Lesson12_多态
- 雅虎财经api_带有Yahoo API的Android反向地理编码– PlaceFinder
- postfix + dovecot + mysql 创建虚拟用户(二)
- Tokyo Cabinet及Tokyo Tyrant tcb tch比较分析
- Sql同一字段中相同数据的数量统计
- RedHat Linux 9.0系统的安装(图文并茂) 下载地址在最后面
- 漫谈程序员系列:谁是为加班而生的
- 开放世界--OpenWorld