# 概述

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

# 二.使用 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

# 三.使用 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

# 四.函数式和 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

# 五.使用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

# 参考

https://blog.csdn.net/qq_36990322/article/details/109858890