React Refs揭密

什么是Ref

React的官方介绍是这样的:

In the typical React dataflow, props are the only way that parent components interact with their children. To modify a child, you re-render it with new props. However, there are a few cases where you need to imperatively modify a child outside of the typical dataflow. The child to be modified could be an instance of a React component, or it could be a DOM element. For both of these cases, React provides an escape hatch.

其中提到了这几个概念:

在典型的React数据流理念中,父组件跟子组件的交互都是通过传递属性(properties)实现的。如果父组件需要修改子组件,只需要将新的属性传递给子组件,由子组件来实现具体的绘制逻辑。

在特殊的情况下,如果你需要命令式(imperatively)的修改子组件,React也提供了应急的处理办法–Ref。

Ref 既支持修改DOM元素,也支持修改自定义的组件。


什么是声明式编程(Declarative Programming)

值得一提的是当中声明式编程(Declarative Programming)和命令式编程(Imperative Programming)的区别。声明式编程的特点是只描述要实现的结果,而不关心如何一步一步实现的,而命令式编程则相反,必须每个步骤都写清楚。我们可以根据语义直观的理解代码的功能是:针对数组的每一个元素,将它的值打印出来。不必关心实现其的细节。而命令式编程必须将每行代码读懂,然后再整合起来理解总体实现的功能。

React有2个基石设计理念:一个是声明式编程,一个是函数式编程。函数式编程以后有机会再展开讲。声明式编程的特点体现在2方面:

组件定义的时候,所有的实现逻辑都封装在组件的内部,通过state管理,对外只暴露属性。

组件使用的时候,组件调用者通过传入不同属性的值来达到展现不同内容的效果。一切效果都是事先定义好的,至于效果是怎么实现的,组件调用者不需要关心。

因此,在使用React的时候,一般很少需要用到Ref。那么,Ref的使用场景又是什么?

Ref使用场景

React官方文档是这么说的:

There are a few good use cases for refs: Managing focus, text selection, or media playback.Triggering imperative animations.Integrating with third-party DOM libraries. Avoid using refs for anything that can be done declaratively.

简单理解就是,控制一些DOM原生的效果,如输入框的聚焦效果和选中效果等;触发一些命令式的动画;集成第三方的DOM库。最后还补了一句:如果要实现的功能可以通过声明式的方式实现,就不要借助Ref。

通常我们会利用 render 方法得到一个 App 组件的实例,然后就可以对它做一些操作。但在组件内,JSX 是不会返回一个组件的实例的,它只是一个ReactElement,只是告诉你,React被挂载的组件应该什么样:

1
const myApp = <App />

refs就是由此而生,它是React组件中非常特殊的props, 可以附加到任何一个组件上,从字面意思上看,refs即reference,组件被调用时会创建一个该组件的实例,而refd就会指向这个实例。

Ref用法

如果作用在原生的DOM元素上,通过Ref获取的是DOM元素,可以直接操作DOM的API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class CustomTextInput extends React.Component {  
constructor(props) {
super(props);
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
if(this.myTextInput !== null) {
this.textInput.current.focus();
}
}
render() {
return (
<div>
<input type="text" ref={(ref) => this.myTextInput = ref} />
<input type="button" value="Focus the text input" onClick={this.focusTextInput}/>
</div>

);
}
}

如果作用在自定义组件,Ref获取的是组件的实例,可以直接操作组件内的任意方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 class AutoFocusTextInput extends React.Component {  
constructor(props) {
super(props);
this.textInput = React.createRef();
}
componentDidMount() {
this.textInput.current.focusTextInput();
}
render() {
return (
<CustomTextInput ref={this.textInput} />
);
}
}

Ref总结

为了防止内存泄漏,当卸载一个组件时,组件里所有的refs就会变成null。

值得注意的是,findDOMNoderefs 都无法用于无状态组件中。因为,无状态组件挂载时只是方法调用,并没有创建实例。

对于 React 组件来讲,refs 会指向一个组件类实例,所以可以调用该类定义的任何方法。如果需要访问该组件的真实 DOM ,可以用 ReactDOM 。 findDOMNode来找到 DOM 节点,但并不推荐这样做,因为这大部分情况下都打破了封装性,而且通常都能用更清晰的方法在React中构建代码。

本文结束感谢您的阅读

本文标题:React Refs揭密

文章作者:陈宇(cosyer)

发布时间:2019年02月17日 - 23:02

最后更新:2020年05月13日 - 23:05

原始链接:http://mydearest.cn/React%20Refs%E5%92%8CDOM%E6%8F%AD%E5%AF%86.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!