我们习惯对类组件使用ref,指向类组件实例。我们可以调用类组件实例上面的方法。

    1. import React from 'react';
    2. class Child extends React.Component {
    3. log() {
    4. console.log('test');
    5. }
    6. render() {
    7. return null;
    8. }
    9. }
    10. class App extends React.Component {
    11. childEl = React.createRef();
    12. componentDidMount() {
    13. this.childEl.current.log();
    14. }
    15. render() {
    16. return <Child ref={this.childEl} />;
    17. }
    18. }
    19. export default App;

    组件有些特定属性是不会放在props中的,比如key,比如ref。

    比如下面的类组件中从props获取ref会warn。这是因为 ref 不是 prop 属性。就像 key 一样,其被 React 进行了特殊处理。

    1. import React from 'react';
    2. class Child extends React.Component {
    3. componentDidMount() {
    4. console.log(this.props.ref);
    5. }
    6. render() {
    7. return null;
    8. }
    9. }
    10. class App extends React.Component {
    11. childEl = React.createRef();
    12. render() {
    13. return <Child ref={this.childEl} />;
    14. }
    15. }
    16. export default App;

    React面试题:函数式组件如何调用子组件方法? - 图1

    所以你无法从props中获取ref属性。

    另外如果我们给函数式组件设置ref属性会提示warning。

    1. import React from 'react';
    2. const Child = () => null;
    3. class App extends React.Component {
    4. childEl = React.createRef();
    5. render() {
    6. return <Child ref={this.childEl} />;
    7. }
    8. }
    9. export default App;

    React面试题:函数式组件如何调用子组件方法? - 图2

    而且函数式组件也没法挂载方法。

    这也有问题,那也有问题,听起来不太妙。不过还是有解决办法。

    问题:如果我们有一个子组件是函数式组件,想调用它的方法应该怎么办?

    首先应该让组件接收到ref属性。

    如果你希望给子组件传递ref,那就需要用到React.forwardRef方法,即转发ref。

    例如

    1. import React from 'react';
    2. const Child = React.forwardRef((props, ref) => (
    3. <input ref={ref} />
    4. ));
    5. class App extends React.Component {
    6. childEl = React.createRef();
    7. componentDidMount() {
    8. this.childEl.current.focus();
    9. }
    10. render() {
    11. return <Child ref={this.childEl} />;
    12. }
    13. }
    14. export default App;

    上面这段代码中,App给子组件Child传ref参数,Child被React.forwardRef包装,对ref属性处理后传递给里面的函数(第二个参数),这样函数组件就可以获取到ref属性了。

    (函数式组件传递ref给自己的子元素,让父组件可以获取子元素的引用是forwardref比较常见的应用场景)

    函数式组件有了ref方法,那么应该让ref上面绑定上方法以便父组件调用呢?

    答案是使用useImperativeHandle钩子对接收到的ref属性进行处理,给它挂载上一些方法。

    看下面的例子

    1. import React, {useImperativeHandle} from 'react';
    2. const Child = React.forwardRef((props, ref) => {
    3. useImperativeHandle(ref, () => ({
    4. log: () => {
    5. console.log('test');
    6. }
    7. }));
    8. return null;
    9. });
    10. class App extends React.Component {
    11. childEl = React.createRef();
    12. componentDidMount() {
    13. this.childEl.current.log();
    14. }
    15. render() {
    16. return <Child ref={this.childEl} />;
    17. }
    18. }
    19. export default App;

    这个例子实现了和最开始的类组件相同的效果。

    总结:如果习惯使用函数式组件,还有调用组件方法的需求,那么可以使用React.forwardRef+ useImperativeHandle来实现。