(给前端大全加星标,提升前端技能)

英文:Yomi Eluwande  译文:joking_zhang

https://segmentfault.com/a/1190000019277029

使用 React 时,我们的默认思维方式应该是 不会强制修改 DOM ,而是通过传入 props 重新渲染组件。但是,有些情况却无法避免修改 DOM 。

React 中的 Refs 提供了一种访问 render() 方法中创建的 React 元素(或 DOM 节点)的方法。

当父组件需要与子组件交互时,我们通常使用 props 来传递相关信息。 但是,在某些情况下,我们可能需要修改子项,而不用新的props 重新呈现 (re-rendering) 它。 这时候就需要 refs 出场了。

我什么时候应该使用 Refs ?

我们建议在以下情况下使用 refs:

  • 与第三方 DOM 库集成

  • 触发命令式动画

  • 管理焦点,文本选择或媒体播放

译注:第三点是否也可以理解为使用 event 对象呢?在 React 中就是合成事件(SyntheticEvent)。
官方文档中提到:避免使用 refs 来做任何可以通过声明式实现来完成的事情。

所以一旦我们确定我们真的应该使用 refs,我们需要如何使用它们呢?

在 React 中使用 Refs

您可以通过多种方式使用 refs :

  • React.createRef()

  • 回调引用 (Callback refs)

  • String refs(已过时)

  • 转发 refs (Forwarding refs)

接下来,让我们看看每一种实现方式:

React.createRef()

可以使用该 React.createRef() 函数创建 Refs ,并通过该 ref 属性附加到 React 组件中的 HTML 元素。

通常在组件的构造函数内创建 ref ,使其在整个组件中可用。例如:

class MyComponent extends React.Component {  constructor(props) {super(props);this.firstRef = React.createRef();}  render() {return <div ref={this.firstRef} />;}}

如上所示:

  • 一个 ref 实例在构造函数中创建,并赋值给 this.firstRef

  • 在 render() 方法内部,将构造函数中创建的 ref 传递给 div

接下来,让我们看一个在 React 组件中使用 refs 的示例。

使用 Refs 聚焦输入

这是另一个例子:

// Ref.jsclass 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  ref// with the `textInput` that we created in the constructorreturn (
<input type="text" ref={this.textInput} /><input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}/>div>);}}

在上面的代码块中,我们构建了一个按钮,当单击它时,该页面会自动聚焦在输入框上。

首先,我们在构造方法中创建一个 ref 实例,并将其赋值给 this.textInput,然后通过 ref 属性将其分配给 input 元素。

 type="text" ref={this.textInput} />

注意,当 ref 属性被一个 HTML 元素使用时(比如当前示例中的 input 元素),在 constructor 中使用 React.createRef() 创建的 ref 会接收来自底层 DOM 元素的 current 值。

译注:这里的 current 应该是 合成事件(SyntheticEvent)

这意味着访问 DOM 值,我们需要写这样的东西:

this.textInput.current;

第二个元素是一个按钮,点击它之后会自动聚焦到第一个输入框上面。我们为 onClick 属性设置了 this.focusTextInput 函数。

type="button"value="Focus the text input"onClick={this.focusTextInput}/>

函数 focusTextInput() 使用了 JavaScript 构建 DOM 的标准函数。 .focus() 方法会将光标聚焦于文本输入框上。

focusTextInput() {this.textInput.current.focus();}

最后,focusTextInput 函数绑定在这样的 constructor 方法中的:

this.focusTextInput = this.focusTextInput.bind(this);

从 ref 中获取值

在这个例子中,我们将看到如何为 input 输入框设置 ref 属性,并通过 ref 来获取值。示例如下:

在这个例子中,我们创建了一个 input 输入框来输入值。然后,当单击提交按钮时,我们将读取此值,并在控制台打印。

// Ref.jsclass CustomTextInput extends React.Component {  constructor(props) {super(props);// create a ref to store the textInput DOM elementthis.textInput = React.createRef();}  handleSubmit = e => {    e.preventDefault();    console.log(this.textInput.current.value);};  render() {// tell React that we want to associate the  ref// with the `textInput` that we created in the constructorreturn (
<form onSubmit={e => this.handleSubmit(e)}><input type="text" ref={this.textInput} />Submitbutton>form>div>);}}

同样,我们使用该 React.createRef() 函数创建一个 ref 实例,然后将它分配给实例变量 this.textInput。

在 render 函数中,我们希望读取 form 下输入框的值。我们如何读取这个值? 通过为 input 指定一个 ref ,然后读取 ref 的值。

 type="text" ref={this.textInput} />

点击提交按钮,上面示例中 form 元素会通过 onSubmit 方法,调用 this.handleSubmit 函数 ,并在控制台打印输入框中的信息。

handleSubmit = e => {  e.preventDefault();  console.log(this.textInput);};

上面,参数 e 包含事件对象。我们使用e.preventDefault() 来告诉浏览器我们正在处理被点击的提交按钮,我们不希望这个事件“冒泡”(意思就是说,阻止浏览器的默认行为)。
译注:这里可以看一下 React 对于事件的处理:在 React 中另一个不同点是你不能通过返回 false 的方式阻止默认行为。你必须显式的使用 preventDefault

在上面示例中,我们打印了 this.textInput ,在控制台可以看到一个 ref 对象。

> Object {current: HTMLInputElement}

请注意,它有一个 current属性,即 HTMLInputElement 。这是 input DOM 元素本身,而不是实际值。 我们必须使用 this.textInput.current.value 来获取 input 标签的实际值:

handleSubmit = e => {  e.preventDefault();  console.log(this.textInput.current.value);};

使用 refs 是一种从表单中直接提取值的方式:只需要给 input 标签设置 ref ,并在你需要的时候将值提取出来。

Refs 回调

Refs 回调 是在 React 中使用 ref 的另一种方式。要以这种方式使用 ref,我们需要为 ref 属性设置回调函数。当我们设置 ref 时,React 会调用这个函数,并将 element 作为第一个参数传递给它。

这是另一个例子的代码。像上面的示例一样,此代码获取 input 标签的文本值,但在这里我们使用回调引用:

// Refs.jsclass CustomTextInput extends React.Component {  constructor(props) {super(props);this.textInput = null;this.setTextInputRef = element => {this.textInput = element;};}  handleSubmit = e => {    e.preventDefault();    console.log(this.textInput.value);};  render() {return (
<form onSubmit={e => this.handleSubmit(e)}><input type="text" ref={this.setTextInputRef} />Submitbutton>form>div>);}}

上面的示例中,我们将 input 标签的 ref 设置为 this.setTextInputRef。

当组件安装时,React 会将 DOM 元素传递给 ref 的回调;当组件卸载时,则会传递 null。(ref 回调会在 componentDidMount 和 componentDidUpdate 生命周期之前调用。)

String Ref(已过时)

还有另一种设置 refs 的方法,但它被认为是过时的,可能很快就会被弃用。但是你可能会在其他人的代码中看到它,所以这里说一下。

使用 string refs,你将会看到这样的 input 标签:

 type="text" ref="textInput" />

然后,我们可以在组建上得到这样的值:this.refs.textInput.value - 但是,再次声明,这不应该在新代码中使用,因为这个 API 将被弃用。

转发 Refs (Forwarding Refs)

Ref forwarding 是一种将 ref 通过组件传递给其子节点的技术。它对于可复用组件库和高阶组件(HOC)等情况非常有用。

您可以使用 React.forwardRef 函数将 ref 转发到组件。我们来看下面的例子:

// Ref.jsconst TextInput = React.forwardRef((props, ref) => (<input type="text" placeholder="Hello World" ref={ref} />));const inputRef = React.createRef();class CustomTextInput extends React.Component { handleSubmit = e => { e.preventDefault(); console.log(inputRef.current.value);}; render() {return (
<form onSubmit={e => this.handleSubmit(e)}><TextInput ref={inputRef} />Submitbutton>form>div>);}}

Ref forwarding 允许组件接收一个 ref ,并将它向下传递(换句话说,“转发”它)给子组件。

在上面的示例中,我们使用 input 标签创建了一个名为 TextInput 的组件。那么,我们如何将 ref 传递或转发到 input 标签呢?

首先,我们使用下面的代码创建一个 ref :

const inputRef = React.createRef();

然后,我们将 ref 通过为组件 指定一个同名的 JSX 的属性,将 ref 向下传递。然后 React 将会把 ref 作为第二个参数转发给 forwardRef 函数。

接下来,我们将此 ref 参数转发给 。现在可以在外层组件通过 inputRef.current 访问DOM节点的值了。

转发 refs 和高阶组件

最后,让我们看一下使用 refs 的另一个例子,但这次是使用高阶组件(HOC)。

在上面的示例应用程序中,会将所有 input 标签中输入的值在控制台打印。这里已经为 input 标签设置了 ref 属性,接下来,让我们看一下需要如何在高阶组件中传递 / 转发 ref 。

const Input = InputComponent => {const forwardRef = (props, ref) => {const onType = () => console.log(ref.current.value);return <InputComponent forwardedRef={ref} onChange={onType} {...props} />;};return React.forwardRef(forwardRef);};

这里有一个名为 Input 的高阶组件 ,它接受 InputComponent 作为参数。当用户输入的时候,他还会将 ref 的值在控制台打印。

在 Input 高阶组件内,forwardRef 函数会返回 InputComponent。forwardRef 函数中所包含的 ref 参数,是由 React.forwardRef 函数创建的。 高阶组件最终会将包装好的组件作为值返回。

接下来,我们创建一个组件,将 input 作为子组件包含进来。

const TextInput = ({ forwardedRef, children, ...rest }) => (
<input ref={forwardedRef} {...rest} />{children}div>);

上面的组件会将 forwardedRef 分配给 ref 属性, 当渲染子组件的时候,input 输入框就会接收到这个 ref 。…rest 是 props 的解构(也就是说,我们会将 rest 数组中的所有参数作为 props 传递给 input 组件)。那么我们该如何使用 TextInput 组件呢?像这样:

const InputField = Input(TextInput);class CustomTextInput extends Component { render() {const inputRef = React.createRef();return <InputField ref={inputRef} />;}}

最后,将 TextInput 传入 Input 高阶组件,会返回一个 InputField component。

创建一个 ref ,并作为参数传递给 InputField 组件。

结论

与通过 props 和 state 不同,Refs 是一种将数据传递给特定子实例的好方法。

你必须要小心,因为 refs 操纵实际的 DOM,而不是虚拟的 DOM,这与 React 思维方式相矛盾。因此,虽然 refs 不应该是通过应用程序流动数据的默认方法,但是当您需要时,它们是可以从 DOM 元素读取数据的好方法。

推荐阅读

(点击标题可跳转阅读)

看完这篇,你也可以实现一个360度全景插件

从 Mixin 到 HOC 再到 Hook

深入分析虚拟DOM的渲染过程和特性

觉得本文对你有帮助?请分享给更多人

关注「前端大全」加星标,提升前端技能

好文章,我在看❤️

react 点击使父元素消失_在 React 组件中使用 Refs 指南相关推荐

  1. react 点击使父元素消失_React 基础:Refs 和 DOM 引用之间的关系

    前言 这系列是 React 基础教程(参考 React 官网),记录了自己入门学习 React 的笔记.不太适合有 React 丰富经验的同学,但希望看到此文的你,多少都有些收获. 文章代码均可在我的 ...

  2. css设置元素继承父元素宽度_详解CSS中的百分比的应用

    前言 百分比的应用随处可见,但是就一直没有机会去好好总结一下,如今项目中遇到的坑都是当年留的泪,在月底之前终于把这个好久想总结的文章给写完了. 1.使用百分比的场合 在目前项目中,最常用百分比的莫过于 ...

  3. 当子元素用position:relative;时,父元素的overflow:hidden;在ie中失效的解决办法

    当子元素用position:relative;时,父元素的overflow:hidden;在ie中失效的解决办法: 给父元素也加上position:relative; 到现在也不知道为什么会出现这样的 ...

  4. ztree在刷新时第一个父节点消失_从反向传播推导到梯度消失and爆炸的原因及解决方案(从DNN到RNN,内附详细反向传播公式推导)...

    引言:参加了一家公司的面试和另一家公司的笔试,都问到了这个题!看来很有必要好好准备一下,自己动手推了公式,果然理解更深入了!持续准备面试中... 一. 概述: 想要真正了解梯度爆炸和消失问题,必须手推 ...

  5. css设置元素继承父元素宽度_前端新手必知-5种新型的CSS长度单位

    众所周知CSS技术我们虽然很熟悉,在使用的过程却很容易被困住,这让我们在新问题出现的时候变得很不利.随着web继续不断地发展,对于新技术新解决方案的要求也会不断增长. 因此,作为网页设计师和前端开发人 ...

  6. react前端封装接口弹出错误_在react项目中用es6封装ajax请求,组件中调用总是报错,求解?...

    函数代码如下class networkEngine{ get(req){ req.type = 'get' ; req.dataType = 'json' ; req.cache = 'false'  ...

  7. dom渲染完毕再渲染数据_在vue组件中,异步手动渲染dom

    在业务中,页面会动态的渲染组件,组件中会根据数据,去特殊处理渲染dom元素,当遇到需要异步的去渲染时,会出现异步的问题无法正常显示.如下,重新生成一个组件进行渲染. 使用基础 Vue 构造器,创建一个 ...

  8. Vue 点击获得父元素,子元素,兄弟元素(DOM操作)

    <ul @click ="clickfun($event)"><li></li> </ul>methods: {clickfun(e ...

  9. 怎么样使父元素的overflow:hidden不影响到子级absolute绝对定位元素

    在项目中使用了popover组件,由于父级设置了overflow:hidden,当子级长度超出时,即使子元素是绝对定位元素也受到了影响. 查了很多资料,发现只要父级元素不设置position:rela ...

  10. ztree在刷新时第一个父节点消失_第一个关于中式菜谱的智能问答机器人小程序正式上线啦...

    为了满足大家对菜品烹饪的各类问题能直接得到答案的需求,我开发了目前第一个真正关于菜谱的智能问答系统,并在微信小程序发布上线.这套系统支持对于8600多种菜品的问答功能,并能实现快速问答响应,整套系统后 ...

最新文章

  1. extern数组与extern指针
  2. 用英语说中国 IOS APP 上线
  3. Stust2的拦截器的运行流程及使用方法、注意事项
  4. Socket TCP和UDP的区别
  5. 怎么用ubuntu进入python_ubuntu 下python环境的切换使用
  6. java读取nfc数据_JAVA有关NFC读卡器读取数据
  7. three.js自定义材质各向异性
  8. 清华牛人总结的数据分析笔记
  9. 最终幻想13-2时钟迷题破解工具
  10. 因特网在线聊天协议(IRCP/IRC)--网络大典
  11. SetFocus 方法
  12. 数据结构与算法(educoder作业)
  13. 蓝桥杯软件类竞赛---手算题攻略
  14. ASP.net的ItemDataBound事件与LinqToSql数据源关于e.Item.DataItem的类型
  15. java字符串转数组的方法,写给正在求职的Java开发
  16. HTML5网页设计基础——音乐盒的制作
  17. IIS 配置允许跨域访问
  18. 基于Android的手机订餐系统设计与实现(一)
  19. 基于MATLAB的LDL分解法
  20. yzh 第七课 RISCV指令集

热门文章

  1. 面试题:老师生日分析过程,能否建模用程序解答?
  2. 9.携程架构实践 --- 网站高可用
  3. 2.微服务:从设计到部署 --- 使用 API 网关
  4. 49. 模型层 --- dao 层
  5. 4. JavaScript Debug Tips
  6. 28. Location replace() 方法
  7. thinkphp5.0.6 连接SQLServer2008r2 配置总结
  8. linux shell中$0,$?,$!等的特殊用法
  9. ajaxFileUpload 异步上传文件简单使用
  10. VMware 虚拟机运行卡慢的解决办法