Refs

1 是什么

Refs 是在 React 中获取节点(原生 DOM 或 React 组件)实例的方式。可以通过 Refs 来获取节点实例的引用,通过操作此引用达到操作节点的目的。

2 解决什么问题

Refs 主要用来解决需要操作节点的场景,如:

  • 管理子节点焦点、文本选择
  • 强制动画
  • 集成第三方 DOM 库

3 使用方法

3.1 createRef

React.createRef 是 React 16.3 版本引入的创建 Refs 的方法。

3.1.1 创建 Refs

通过 React.createRefs() 方法可以创建 Refs。

1
2
3
4
5
6
7
8
9
10
11
class MyComponent extends React.Component {
constructor(props) {
super(props);
// 1、在组件中调用 createRef 函数创建 Refs 变量
this.myRef = React.createRef();
}
render() {
// 2、使用 ref(此属性名固定) 属性将 Refs 变量和节点关联起来
return <div ref={this.myRef} />;
}
}

3.1.2 访问 Refs

通过 Refs 变量的 current 属性可以访问节点的引用。

1
const node = this.myRef.current;// 此时 node 就是节点实例的引用

当组件挂载时,this.myRef.current 会被赋值为节点实例。

当组件被卸载时,this.myRef.current 会被赋值为 null。

3.1.3 示例

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
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
//1、创建 Refs 变量 this.textInput
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}

focusTextInput() {
// 3、通过 this.textInput.current 属性访问节点实例
this.textInput.current.focus();
}

render() {
// 2、通过给节点配置 ref 属性,将节点实例关联到 this.textInput
return (
<div>
<input
type="text"
ref={this.textInput} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}

3.2 callback Refs

callback Refs 是指通过给节点的 ref 属性传递回调函数来获取节点实例。

3.2.1 示例

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
30
31
32
33
34
35
36
37
38
39
40
class CustomTextInput extends React.Component {
constructor(props) {
super(props);

this.textInput = null;

this.setTextInputRef = element => {
//2、回调函数会在节点挂载和卸载时被调用
// 节点的实例被作为参数(如 element)传给回调,卸载时 element 的值为 null
this.textInput = element;
};

this.focusTextInput = () => {
// 3、使用引用来操作节点
if (this.textInput) this.textInput.focus();
};
}

componentDidMount() {
// 组件挂载后,让文本框自动获得焦点
this.focusTextInput();
}

render() {
// 1、给节点的 `ref` 属性配置回调函数 this.setTextInputRef
return (
<div>
<input
type="text"
ref={this.setTextInputRef}
/>
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}

3.2.2 注意

ref 回调可以在组件间传递,进而可以解决嵌套组件间 ref 传递问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function CustomTextInput(props) {
// ref 属性值通过 props 从外部传递进来
return (
<div>
<input ref={props.inputRef} />
</div>
);
}

class Parent extends React.Component {
render() {
return (
<CustomTextInput
inputRef={el => this.inputElement = el}
/>
);
}
}

3.3 String Refs(过时的 API)

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
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.focusTextInput = this.focusTextInput.bind(this);
}

focusTextInput() {
// 2、在父节点中通过 this.refs.myInput 属性访问子节点实例
this.refs.myInput.focus();
}

render() {
// 1、通过给子节点配置 ref 属性,值为字符串 myInput
return (
<div>
<input
type="text"
ref="myInput" />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}

如果目前还在使用 this.refs.textInput 这种方式访问 refs ,建议用 callback refs 或 createRefs 的方式代替,String 类型的 Refs 在未来版本可能将会被移除。

4 注意

1、默认情况下,不能在函数组件上使用 ref 属性,因为它没有实例。可以通过 forwardRef 创建函数组件,进而使其可以接收 ref 属性,或者将函数组件转化为 class 组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function MyFunctionComponent() {
return <input />;
}

class Parent extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
render() {
// This will *not* work!
return (
<MyFunctionComponent ref={this.textInput} />
);
}
}