React之Refs的使用

我们在日常写React代码的时候,一般情况是用不到Refs这个东西,因为我们并不直接操作底层DOM元素,而是在render函数里去编写我们的页面结构,由React来组织DOM元素的更新。

凡事总有例外,总会有一些很奇葩的时候我们需要直接去操作页面的真实DOM,这就要求我们有直接访问真实DOM的能力,而Refs就是为我们提供了这样的能力。

看这个名字也知道,`Refs其实是提供了一个对真实DOM(组件)的引用,我们可以通过这个引用直接去操作DOM(组件)

为什么用到refs

为什么会用到这个
上面有提到,我们一般情况下是不需要用到这个东西,那具体什么时候才会用到呢? 看官方建议:

  • Managing focus, text selection, or media playback.
  • Triggering imperative animations.
  • Integrating with third-party DOM libraries.
    简单的来说就是处理DOM元素的focus,文本的选择或者媒体的播放等,以及处罚强制动画或者同第三方DOM库集成的时候。

也就是React无法控制局面的时候,就需要直接操作Refs了。

React V16版本之前,

我们一般都是通过一个回调函数的方式,把当前组件的DOM绑定到一个实例变量上,像下面这样:

class AutoFocusTextInput extends React.Component {constructor(props) {super(props);this.textInput = null;}componentDidMount() {this.textInput.focusTextInput();}render() {return (<CustomTextInput ref={ele => { this.textInput = ele}} />);}
}

在上面的代码中,我们先声明一个值为null的textInput变量,然后在ref中以回调的方式将组件DOM赋值给textInput。然后就可以通过 this.textInput.focus()这样的性质来直接调用CustomTextInput这个组件的实例方法。

但是这个方式有以下两个不太好:

每次组件重新渲染的时候,行内函数都会执行两次,第一次的ele的值为空,第二次才为真正的DOM对象。
因为在每次渲染中React都会创建一个新的函数实例。因此,React 需要清理旧的 ref 并且设置新的。
通过将 ref 的回调函数定义成类的绑定函数的方式可以避免上述问题,

如果我们想要将一个子组件的ref传递给父组件,可能会有点麻烦,虽然通过一个特殊的prop属性可以做到,但是感觉有点不太正规。。。

React V16 版本后

React V16版本新增一个API:React.createRef(); 通过这个API,我们可以先创建一个ref变量,然后再将这个变量赋值给组件声明中ref属性就好了。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>React 实例</title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
<div id="example"></div><script type="text/babel">//方式1
class MyComponent extends React.Component {handleClick() {// 使用原生的 DOM API 获取焦点this.refs.myInput.focus();console.log(this.refs.myInput)}render() {//  当组件插入到 DOM 后,ref 属性添加一个组件的引用于到 this.refsreturn (<div><input type="text" ref="myInput" /><inputtype="button"value="点我输入框获取焦点"onClick={this.handleClick.bind(this)}/></div>);}
}// 方式2
class CustomTextInput extends React.Component {constructor(props) {super(props);// create a ref to store the textInput DOM elementthis.textInput = React.createRef();this.focusTextInput = this.focusTextInput.bind(this);}focusTextInput() {// Explicitly focus the text input using the raw DOM API// Note: we're accessing "current" to get the DOM nodethis.textInput.current.focus();}render() {// tell React that we want to associate the <input> ref// with the `textInput` that we created in the constructorreturn (<div><inputtype="text"ref={this.textInput} /><inputtype="button"value="Focus the text input"onClick={this.focusTextInput}/></div>);}
}ReactDOM.render(<CustomTextInput />,document.getElementById('example')
);
</script></body>
</html>

不同之处

在V16版本前,我们可以直接通过变量访问元素的方法,在V16后,我们需要通过 this.textInput.current,即真实的DOM是通过current属性来引用的。

如果通过 createRef()这个API赋值给组件的ref,那么引用的就是组件实例;如果是DOM元素,那引用的自然的就是DOM元素了。。

传递Refs

前面我们说到,在V16版本之前,我们想要父组件拿到子组件的ref,需要通过一些特殊的方法,V16版本之后,React提供了一种原生的方式来完成这种操作。

这就涉及到React新增的另一个API: React.forwardRef(), 通过接受一个函数,来传递refs,具体如下:

const FancyButton = React.forwardRef((props, ref) => (<button ref={ref} className="FancyButton">{props.children}</button>
));// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
  • 首先我们通过React.createRef();创建一个ref变量,然后在FancyButton属性中通过 ref={ref}的方式把这个ref和组件关联起来。
  • 目前为止,如果FancyButton 是一个通过class或者函数声明的组件,那么就到此为止,我们可以说 ref变量的current属性持有对 FancyButton组件实例的引用。
  • 不幸的是,FancyButton经过了 React.forwardRef的处理, 这个API接受两个参数,第二个参数就是ref,然后通过 <button ref={ref}>把ref绑定到button元素上,这样ref.current的引用就是button元素这个DOM对象了。。。

上面的有点绕,简单来说,就是我们创建一个引用,本来是给外面的FancyButton组件的,但是因为React.forwardRef的处理,这个引用被传递给了内部的button元素。这样ref.current的引用由本来的FancyButton实例传递到了button元素本身。

在HOC组件中的应用

HOC(higher-order components)高阶组件,简单的说,就是通过组件包裹的方式来提到代码复用,高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。
以下是一个生成高阶组件的函数:

function logProps(WrappedComponent) {class LogProps extends React.Component {render() {return <WrappedComponent {...this.props} />;}}return LogProps;
}

logProps是函数,接受一个组件参数,返回一个包裹参数组件的logProps组件。

下面是用法:

class FancyButton extends React.Component {focus() {// ...}// ...
}// Rather than exporting FancyButton, we export LogProps.
// It will render a FancyButton though.
export default logProps(FancyButton);

我们先声明一个FancyButton的组件,然后将其作为参数传入logProps函数,最后得到的其实是一个LogProps组件。

接下来我们使用refs:

我们通过文件引入FancyButton(其实引入的是LogProps组件)然后createRef并指向FancyButton。 本意是希望引入真正的FancyButton组件,实际上引用的是 外层包裹组件LogProps组件。
我们可以通过以下改造来完善代码:

function logProps(Component) {class LogProps extends React.Component {render() {const {forwardedRef, ...rest} = this.props;// Assign the custom prop "forwardedRef" as a refreturn <Component ref={forwardedRef} {...rest} />;}}// Note the second param "ref" provided by React.forwardRef.// We can pass it along to LogProps as a regular prop, e.g. "forwardedRef"// And it can then be attached to the Component.return React.forwardRef((props, ref) => {return <LogProps {...props} forwardedRef={ref} />;});
}

如面的代码所示,我们修改了高阶组件logProps函数的实现方式,在内部组件LogProps的render方法中,给被包裹组件(作为参数传入的组件)添加了来自props的ref。

最终返回的也是一个React.forwardRef处理过的组件,这个组件将ref传递到内部的props中去。

这样,但我们通过logProps(FancyButton)函数调用的时候,其实返回的是一个经过React.forwardRef处理的组件, 当通过

<FancyButtonlabel="Click Me"handleClick={handleClick}ref={ref}
/>;

去添加ref的时候, 这个ref其实直接添加到了内部的LogProps组件的forwardedRef属性上,然后在LogProps组件内部,又通过props属性的方式被赋值了 被包裹组件(作为参数的组件,也就是FancyButton组件)。这个传递其实经过了三次。。。。

总的来说,高阶组件的ref其实是通过React.forwardRef技术将ref传递到包裹组件logProps上,然后有通过属性传递 传递到真正的FancyButton组件上,两次传递才完成。。。。

参考

  • https://segmentfault.com/a/1190000015113359
  • https://www.jianshu.com/p/56ace3e7f565

React之Refs的使用相关推荐

  1. React中refs的理解

    React中refs的理解 Refs提供了一种方式,允许我们访问DOM节点或在render方法中创建的React元素. 描述 在典型的React数据流中,props是父组件与子组件交互的唯一方式,要修 ...

  2. React 中 refs 的作用是什么?

    Refs 是 React 提供给我们的安全访问 DOM 元素或者某个组件实例的句柄. 我们可以为元素添加 ref 属性然后在回调函数中接受该元素在 DOM 树中的句柄,该值会作为回调函数的第一个参数返 ...

  3. React基础—refs介绍

    1. refs React核心就在于虚拟DOM,也就是在React中不总是直接操作页面真实的DOM元素,并且结合Diffing算法,可以做到最小化页面重绘.最为直观的为: 在使用数组的map方法返回一 ...

  4. react的refs属性

    refs属性的意义是标识,相当于原生js中的id,可以通过this.refs.标识名来访问,获取到的是真实DOM ref可以为: string类型(不推荐,可能会出现问题) 回调函数类型:分为行内式和 ...

  5. React基础 - refs的详解与应用

    文章目录 ref的使用场景 操作原始DOM class组件 1. React.createRef(推荐) 2. 回调函数方式 3. string 方式(不推荐) function组件 操作 React ...

  6. React 之 Refs 的使用和 forwardRef 的源码解读

    三种使用方式 React 提供了 Refs,帮助我们访问 DOM 节点或在 render 方法中创建的 React 元素. React 提供了三种使用 Ref 的方式: 1. String Refs ...

  7. React开发(211):react中refs转发到dom组件

  8. React高级话题之Refs and the DOM

    前言 本文为意译,翻译过程中掺杂本人的理解,如有误导,请放弃继续阅读. 原文地址:Refs and the DOM 正文 Refs提供了一种访问在render方法里面创建的React element或 ...

  9. React的三大属性之refs的一些简单理解

    什么是refs? Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素. Ref转发是一项将ref自动通过组件传递到子组件的技巧. 通常用来获取DOM ...

最新文章

  1. 我用YOLOX露了一手,记录一下模型部署、优化及训练的实现全过程
  2. windows环境下,django + mongoengine + mongodb环境配置
  3. python grequests极限_Python grequests闲话
  4. 前端学习(3016):vue+element今日头条管理--总结
  5. Linkedln技术高管Jay Kreps:Lambda架构剖析
  6. LeetCode 821. 字符的最短距离
  7. 基于javaweb(springboot)城市地名地址信息管理系统设计和实现
  8. linux 图片编辑 java_Java在Linux下 不能处理图形的解决办法 分享
  9. 新华三杯考前突击---Day1---物联网技术篇
  10. MatLab 中计算开根号
  11. 地理加权回归的学习(地理加权回归用来量化空间异质性)
  12. NXP MPC574X eTimer
  13. 【GIS风暴】什么是EPSG?常见坐标系对应的EPSG代号、经度范围、中央经线是多少?
  14. PHP2018人资面试题
  15. c语言编程去掉最大值最小值,C语言最大值最小值编程
  16. SQL Server 升序和降序排列
  17. win10如何查看开机启动项
  18. 不动点求数列通项原理_【数列】浅谈“不动点”求数列通项的方法
  19. python解析HL7协议多方式对比
  20. 跟随郭霖学Volley

热门文章

  1. 郑豪7.19黄金早间亚盘最新操作建议(守住1800上看多,突破1824看波段)
  2. 教你检测服务器公网IP和端口是否连通
  3. 浅谈UpdatePanel
  4. windows 2008 R2 无法更新 WindowsUpdate 80072EE2
  5. SpringBoot 进行sql操作,但是在Druid控制台的SQL监控中没有信息
  6. Android获取网络时间、NTP服务器时间的方法
  7. 如何使用免费的服务器
  8. 今天偶尔逛delphi的论坛,看到的json文章,收藏
  9. 利用不同卫星的仰角方位角绘制站心星空图
  10. Anaconda 克隆环境