在 React 中,父组件调用父组件的方法,一般用 props 回调即可。但是如果需要在父组件中调用子组件的方法?

其实核心思想就是通过 ref 来获取子组件的实例/方法。

类组件

类组件可以通过 ref 获取子组件的实例,从而调用实例的方法。

class Sub extends React.Component {
  func = () => {
    console.log('sub function');
  };
  render() {
    return <div>Hello from Sub</div>;
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.ref = React.createRef();
  }
  handleBtnClick = () => {
    this.ref.current.func();
  };
  render() {
    return (
      <div className="App">
        <Sub ref={this.ref} />
        <button onClick={this.handleBtnClick}>Click Me</button>
      </div>
    );
  }
}

代码示例:codesandbox

函数组件

函数组件对于 ref 的处理,需要使用 forwardRef 来进行 ref 的转发。使用 forwardRef 需要指定 ref 的指向。

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
  const { label, ...otherProps } = props;
  return (
    <label>
      {label}
      <input {...otherProps} ref={ref} />
    </label>
  );
});

上面是最常用的 ref 使用方式,指向一个 DOM 节点,可以获取 DOM 实例。但是这样子无法获取子组件的方法。

好在 React 提供了导出内部方法的钩子 useImperativeHandle

const MyInput = forwardRef(function MyInput(props, ref) {
  const inputRef = useRef(null);

  useImperativeHandle(
    ref,
    () => {
      return {
        focus() {
          inputRef.current.focus();
        },
        scrollIntoView() {
          inputRef.current.scrollIntoView();
        },
      };
    },
    [],
  );

  return <input {...props} ref={inputRef} />;
});

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
  }

  return (
    <form>
      <MyInput placeholder="Enter your name" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}

代码实例:codesandbox

总结

React 父组件想要获取子组件的实例方法,必须通过 ref 来获取。而在类组件和函数组件上有部分区别。

  • 类组件可以直接通过 createRef 创建 ref 指向子组件实例,从而调用子组件内部的方法。
  • 函数组件则需要使用 useImperativeHandle 暴露子组件的方法。

参考链接