React精进之Refs

本文最后更新于:1 个月前

React 精进之Refs

什么是Refs

官方文档这样写道:Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。

在典型的 React 数据流中,props 是父组件与子组件交互的唯一方式。要修改一个子组件,你需要使用新的 props 来重新渲染它。但是,在某些情况下,你需要在典型数据流之外强制修改子组件。被修改的子组件可能是一个 React 组件的实例,也可能是一个 DOM 元素。对于这两种情况,React 都提供了解决办法。

在React 16.3版本引入了React.createRef() API,我们来试试看

创建 Refs

调用React.createRef(),并通过 ref 属性附加到 React 元素。在构造组件时,通常将 Refs 分配给实例属性,以便可以在整个组件中引用它们。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React, {Component} from 'react';

class RefsStudy extends Component {
constructor(props) {
super(props);
this.myRefs = React.createRef();
}

render() {
console.log(this.myRefs) //current:null
return (
<div ref={this.myRef} />
);
}
}

export default RefsStudy;

访问 Refs

当 ref 被传递给 render 中的元素时,对该节点的引用可以在 ref 的 current 属性中被访问。

ref 的值根据节点的类型而有所不同:

  • ref 属性用于 HTML 元素时,构造函数中使用 React.createRef() 创建的 ref 接收底层 DOM 元素作为其 current 属性。
  • ref 属性用于自定义 class 组件时,ref 对象接收组件的挂载实例作为其 current 属性。
  • 你不能在函数组件上使用 ref 属性,因为他们没有实例。

接下来我们分别举例子

为 DOM 元素添加 ref

当ref属性用于HTML元素时,我们就用input来举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import React, {Component} from 'react';

class RefsStudy extends Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
this.clickMe = this.clickMe.bind(this)
}

clickMe() {
this.textInput.current.value = '按钮被点击'
console.log('按钮被点击')
}

componentDidMount() {
console.log(this.textInput.current)
}

render() {
return (
<div>
<input type='text' className='testRefClassName' id='testRefId' ref={this.textInput} />
<button onClick={this.clickMe}>点我</button>
</div>
);
}
}

export default RefsStudy;

点击后,我们可以看见控制台打印出了dom节点和其属性

image-20210822103857866image-20210822103908300

React 会在组件挂载时current 属性传入 DOM 元素,并在组件卸载时传入 null 值。ref 会在 componentDidMountcomponentDidUpdate 生命周期钩子触发前更新。

为 class 组件添加 Ref

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React, {Component} from 'react';
import RefsStudy from "./index";

class RefsStudyWrapper extends Component { // 定义为class的父组件
constructor(props) {
super(props);
this.textInput = React.createRef();
}

componentDidMount() {
console.log(this.textInput.current.textInput.current) // 可以拿到子组件里的dom
this.textInput.current.clickMe() // 当然,也可以使用子组件定义的方法
}

render() {
return (
<div>
<RefsStudy ref={this.textInput}/>
</div>
);
}
}

export default RefsStudyWrapper;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React, {Component} from 'react';

class RefsStudy extends Component { //子组件
constructor(props) {
super(props);
this.textInput = React.createRef();
this.clickMe = this.clickMe.bind(this)
}

clickMe() {
this.textInput.current.value = '按钮被点击'
console.log('按钮被点击')
}

render() {
return (
<div>
<input type='text' className='testRefClassName' id='testRefId' ref={this.textInput} />
<button onClick={this.clickMe}>点我</button>
</div>
);
}
}

export default RefsStudy;

image-20210822104643893

请注意,这仅在 RefsStudy 声明为 class 时才有效


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!