React中父组件调用子组件方法的几种方式

摩森特沃 2021年10月22日 2,287次浏览

转载声明:本文引用自【react中父组件调用子组件的方法】,如有侵权,请联系删除

常用实现方式

方式一:类组件中的使用React.createRef()

  • 优点:
  • 写法简单易懂
  • 缺点:
  • 假如子组件是嵌套了HOC,就无法指向真实子组件
import React , { Component } from "react"

// ---------- 子组件 ----------
class Child extends Component<any, any> {
  func(){
    console.log("执行我")
  }
  render(){
    return (<div>子组件</div>);
  }
}
// ---------- 父组件 ----------
class Parent extends Component<any, any> {
  ChildRef = React.createRef<any>();
  constructor(props) {
    super(props);
  }
  handleOnClick(){
    this.ChildRef?.current?.func?.();
  }
  render(){
    return (<div>
              <button onClick={this.handleOnClick}>click</button>
              <Child ref={this.ChildRef} />	
           </div>);
  }
}

方式二:使用ref回调函数的方式

此方式跟方式一基本相同, 只是创建和设置Ref的方式不同而已

  • 优点:
  • 写法更简单易懂
  • 没有多余的代码
  • 缺点:
  • 假如子组件是嵌套了HOC,就无法指向真实子组件
import React , { Component } from "react"

// ---------- 子组件 ----------
class Child extends Component<any, any> {
  func(){
    console.log("执行我")
  }
  render(){
    return (<div>子组件</div>);
  }
}

// ---------- 父组件 ----------
class Parent extends Component<any, any> {
  ChildRef: any = null
  handleOnClick(){
    this.ChildRef?.func?.();
  }
  render(){
    return (<div>
              <button onClick={this.handleOnClick}>click</button>
	      <Child ref={ node => this.ChildRef = node }/>
	    </div>);
  }
}

方式三:使用props自定义属性传递函数

  • 优点:
  • 写法简单易懂
  • 假如子组件是嵌套了HOC,也可以指向真实子组件
  • 缺点:
  • 需要自定义props属性
import React , { Component } from "react"

// ---------- 子组件 ----------
type ChildProps = {
  onRef?: (node: any) => void;
}
class Child extends Component<ChildProps, any> {
  componentDidMount(){
    this.props?.onRef?.(this);
  }
  func(){
    console.log("执行我")
  }
  render(){
    return (<div>子组件</div>);
  }
}

// ---------- 父组件 ----------
class Parent extends Component<any, any> {
  ChildRef: any = null
  handleOnClick(){
    this.Child?.func?.();
  }
  render(){
    return (<div>
	      <button onClick={this.handleOnClick}>click</button>
	      <Child onRef={ node => this.ChildRef = node } />
	    </div>);
  }
}

方式四:函数式和hooks写法

其实下面的缺点基本不算缺点了,因为函数式写法,下面算是简单的了。使用forwardRef只会让你的组件定义的更复杂

  • 优点:
  • 写法简单易懂
  • 假如子组件嵌套了HOC,也可以指向真实子组件
  • 缺点:
  • 需要自定义props属性
  • 需要自定义暴露的方法
import React, { useImperativeHandle } from 'react';

// ---------- 子组件 ----------
type ChildProps = {
  onRef?: any;
}
const Child: React.FC<ChildProps> = props => {
  //用useImperativeHandle暴露一些外部ref能访问的属性
  useImperativeHandle(props.onRef, () => {
    return {
      func: func,
    };
  });
  const func = () => {
    console.log('执行我');
  }
  return <div>子组件</div>;
};

// ---------- 父组件 ----------
const Parent: React.FC = () => {
  // 以下两种写法均正确
  // const ChildRef = React.createRef();
  const ChildRef: any = useRef();

  function handleOnClick() {
    ChildRef.current?.func?.();
  }

  return (
    <div>
      <button onClick={handleOnClick}>click</button>
      <Child onRef={ChildRef} />
    </div>
  );
};

方式五:使用forwardRef结合高阶组件转发子组件的ref

import React from 'react';

// ---------- 子组件 ----------
// 自定义可以抛出子组件ref的HOC
const withChild: (Comp: any) => any = Comp => {
  return React.forwardRef((props, ref) => {
    return <Comp ref={ref} {...props} />;
  });
}

class Child extends Component {
  func = () => {
    console.log('打印了我');
  };
  render() {
    return <div>我是个测试的子组件</div>;
  }
}

const ChildWrapper = withChild(Child);

// ---------- 父组件 ----------
const Parent = () => {
  // 以下两种写法均正确
  // const ChildRef = React.createRef();
  const ChildRef: any = useRef();

  function handleOnClick() {
    ChildRef.current?.func?.();
  }

  return (
    <div>
      <button onClick={handleOnClick}>click</button>
      <Child ref={ChildRef} />
    </div>
  );
};

方式六:使用forwardRef结合装饰器转发子组件的ref

import React, { Component } from 'react';

// ---------- 子组件 ----------
// 自定义可以抛出子组件ref的HOC
const Log: (Comp: any) => any = Comp => {
  const Wrapper = (props: any) => {
    const {forwardRef, ...rest} = props;
    return <Comp ref={forwardRef} {...rest} />;
  };

  return React.forwardRef((props, ref) => {
    return <Wrapper {...props} forwardRef={ref} />;
  });
}

@Log
class Child extends Component<any, any> {
  function func() {
    console.log('打印了我');
  };
  render() {
    return <div>我是个测试的子组件</div>;
  }
}

// ---------- 父组件 ----------
const Parent = () => {
  // 以下两种写法均正确
  // const ChildRef = React.createRef();
  const ChildRef: any = useRef();

  function handleOnClick() {
    ChildRef.current?.func?.();
  }

  return (
    <div>
      <button onClick={handleOnClick}>click</button>
      <Child ref={ChildRef} />
    </div>
  );
};

总结

  • 父组件调子组件函数有两种情况
  • 子组件无HOC嵌套
  • 有HOC嵌套

选择什么方法,具体情况具体分析。使用onRef自定义props的方式更实用,无论组件是否需要嵌套HOC,嵌套多少层,一把梭就行了,不用改组件里边的代码。一个写好的组件,只需要做加法就好了

引用参考