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的基本使用相关推荐

  1. 一文搞懂 React ref

    最近也开始研究React,这篇文章主要是讲述 Ref 相关的内容,如有错误请指正. ref 的由来 在典型的 React 数据流中,props 是父组件与子组件交互的唯一方式.要修改一个子组件,你需要 ...

  2. React ref用法

    React ref 官方文档 1.介绍 Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素. DOM 节点:就是平常所说的能在页面中直接获取到的原生 ...

  3. React ref的转发

    在前面学习ref时讲过,ref不能应用于函数式组件: 因为函数式组件没有实例,所以不能获取到对应的组件对象 但是,在开发中我们可能想要获取函数式组件中某个元素的DOM,这个时候我们应该如何操作呢? 方 ...

  4. React ref useRef 完全指南

    在这篇文章中,你将学习如何使用React.useRef()钩子来创建持久的可变值(也称为references或refs),以及访问DOM元素. 我们将从下面几点讲解: 1. 可变值 - 1.1用例:记 ...

  5. ReactJS学习系列课程(React ref的使用)

    在我学习React的过程当中,总会被一些名词搞得晕头转向,但是细想起来又非常简单,比如React定义的这个refs,其实就是用于获取dom的一种方式. 在React中组件并不是真实的 DOM 节点,而 ...

  6. react ref无法获取被高阶组件包装的原始组件问题

    问题描述: react无法通过ref获取被高阶组件包装的原始组件 通过ref调用被dva connect包裹的组件报错 我们在平时使用ref获取一个组件的引用后,就可以直接通过ref调用组件自身的函数 ...

  7. React Ref 的使用

    React 提供了 refrefref 属性,让我们可以引用组件的实例或者原生 DOM 元素.使用 refrefref,可以在父组件中调用子组件暴露出来的方法,或者调用原生 element 的 API ...

  8. 3.0 react ref 使用 读取子组数据 转发forword

    1 定义 实例 类中: this.r1 = React.createRef();//实例一个ref , 包含current属性渲染完以后可以用 this.xx.current 来获取dom 回调 类= ...

  9. (精华)2020年7月26日 React ref的三种方式

    import React from 'react'export default class RefDemo extends React.Component {constructor() {super( ...

最新文章

  1. 力扣(LeetCode)刷题,简单题(第6期)
  2. 互联网的中层管理,一个庞大且易脆的群体
  3. 在隐私的博弈时代,BCH为你保驾护航
  4. android工程师 腾讯,腾讯音乐Android工程师一面面试题记录,拿走不谢!
  5. 【错误记录】Android NDK 错误排查记录 ( error: undefined reference to | Linking CXX shared library FAILED )
  6. Linux驱动小技巧 | 利用DRIVER_ATTR实现调用内核函数
  7. GTK的.NET的函数库 GTK#
  8. Velox将在Pangolin上启动其算法交易机器人,并计划推出更多DeFi解决方案
  9. 郑州大学远程教育c语言程序设计答案,郑州大学远程教育C语言考试试卷.doc
  10. mysql 备份工具简介
  11. poj 1325 Machine Schedule 匈牙利二分匹配 基础
  12. Interesting Finds: 2008.01.04
  13. 事业单位资产管理系统破解资产管理难题,实现账、卡、物、地、人相符
  14. android dns 设置,安卓手机怎么设置DNS Android手机修改DNS图文教程
  15. web网站添加ico图标
  16. HandBrake视频压缩工具
  17. ntpdate离线安装
  18. 人人可以是好厨子:72招做饭技巧帮你变大厨
  19. 学生喂养三种宠物:猫、狗和鸟
  20. 肺结节圆形边界光滑_肺结节不是肺癌,千万别恐慌

热门文章

  1. windows10上运行magic keyboard和magic mouse
  2. Win7 TAP-Windows Adapter V9提示Windows 要求已数字签名的驱动程序
  3. Lesson12_多态
  4. 雅虎财经api_带有Yahoo API的Android反向地理编码– PlaceFinder
  5. postfix + dovecot + mysql 创建虚拟用户(二)
  6. Tokyo Cabinet及Tokyo Tyrant tcb tch比较分析
  7. Sql同一字段中相同数据的数量统计
  8. RedHat Linux 9.0系统的安装(图文并茂) 下载地址在最后面
  9. 漫谈程序员系列:谁是为加班而生的
  10. 开放世界--OpenWorld