# 概述
react 中,我们经常会想到,通过父元素直接调用子元素的方法,实现高内聚的效果。
# 一.类组件中的使用 React.createRef()
优点:写法简单易懂
缺点:假如子组件是嵌套了 HOC(高阶组件),就无法指向真实子组件
import React, { Component } from "react";
class Child extends Component {
func() {
console.log("执行我");
}
render() {
return <div>子组件</div>;
}
}
class Parent extends Component {
constructor(props) {
super(props);
this.Child = React.createRef();
}
handleOnClick() {
this.Child.func();
}
render() {
return (
<div>
<button onClick={this.handleOnClick}>click</button>
<Child ref={this.Child}></Child>
</div>
);
}
}
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
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
# 二.使用 ref 的函数式声明
优点:写法更简单易懂;没有多余的代码
缺点:假如子组件是嵌套了 HOC,就无法指向真实子组件
import React, { Component } from "react";
class Child extends Component {
func() {
console.log("执行我");
}
render() {
return <div>子组件</div>;
}
}
class Parent extends Component {
handleOnClick() {
this.Child.func();
}
render() {
return (
<div>
<button onClick={this.handleOnClick}>click</button>
<Child ref={(node) => (this.Child = node)}></Child>
</div>
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 三.使用 props 自定义 onRef 属性
优点:写法简单易懂;假如子组件是嵌套了 HOC,也可以指向真实子组件 缺点:需要自定义 props 属性
import React, { Component } from "react";
import { withRouter } from "react-router-dom";
// 使用装饰器给裹上一层高阶函数(装饰器需要安装对应的 babel 包,此处作为演示使用)
@withRouter
class Child extends Component {
componentDidMount() {
this.props.onRef && this.props.onRef(this);
}
func() {
console.log("执行我");
}
render() {
return <div>子组件</div>;
}
}
class Parent extends Component {
handleOnClick() {
this.Child.func();
}
render() {
return (
<div>
<button onClick={this.handleOnClick}>click</button>
<Child onRef={(node) => (this.Child = node)}></Child>
</div>
);
}
}
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
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
# 四.函数式和 hooks 写法
优点:写法简单易懂;假如子组件嵌套了 HOC,也可以指向真实子组件
缺点:需要自定义 props 属性;需要自定义暴露的方法
// 父组件
import React from "react";
import Child from "./Child";
const Parent = () => {
let ChildRef = React.createRef();
function handleOnClick() {
ChildRef.current.func();
}
return (
<div>
<button onClick={handleOnClick}>click</button>
<Child onRef={ChildRef} />
</div>
);
};
export default Parent;
// 子组件
import React, { useImperativeHandle } from "react";
import { withRouter } from "react-router-dom";
const Child = (props) => {
//用useImperativeHandle暴露一些外部ref能访问的属性
useImperativeHandle(props.onRef, () => {
return {
func: func,
};
});
function func() {
console.log("执行我");
}
return <div>子组件</div>;
};
export default withRouter(Child);
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
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
# 五.使用forwardRef抛出子组件的ref
这个方法更适合自定义 HOC。但问题是,withRouter、connect、Form.create 等方法并不能抛出 ref,假如 Child 本身就需要嵌套这些方法,那基本就不能混着用了。forwardRef 本身也是用来抛出子元素,如 input 等原生元素的 ref 的,并不适合做组件 ref 抛出,因为组件的使用场景太复杂了。下面看代码实现:
// 父组件
import React from "react";
import Child from "./Child";
const Parent = () => {
let ChildRef = React.createRef();
function handleOnClick() {
ChildRef.current.func();
}
return (
<div>
<button onClick={handleOnClick}>click</button>
<Child ref={ChildRef} />
</div>
);
};
export default Parent;
// 子组件
import React, { Component } from "react";
@Log
class Child extends Component {
func = () => {
console.log("打印了我");
};
render() {
return <div>我是个测试的子组件</div>;
}
}
// 自定义可以抛出子组件 ref 的 HOC
function Log(Comp) {
const Log = (props) => {
const { forwardRef, ...rest } = props;
return <Comp ref={forwardRef} {...rest} />;
};
return React.forwardRef((props, ref) => {
return <Log {...props} forwardRef={ref} />;
});
}
export default Child;
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
41
42
43
44
45
46
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
41
42
43
44
45
46
# 参考
https://blog.csdn.net/qq_36990322/article/details/109858890