zoukankan      html  css  js  c++  java
  • react中父组件调用子组件的方法

    1.直接使用ref进行获取

      

    import React, {Component} from 'react';
    
    export default class Parent extends Component {
        render() {
            return(
                <div>
                    <Child onRef={this.onRef} />
                    <button onClick={this.click} >click</button>
                </div>
            )
        }
    
        onRef = (ref) => {
            this.child = ref
        }
    
        click = (e) => {
            this.child.myName()
        }
    
    }
    
    class Child extends Component {
        componentDidMount(){
            this.props.onRef(this)
        }
    
        myName = () => alert('xiaohesong')
    
        render() {
            return ('woqu')
        }
    }
    

      

    2.假如是子组件是高阶组件的话,通过ref获取子组件实例方法。

    1. 子组件需要获取父组件的信息,这通过props就可以解决;
    2. 父组件需要知道子组件的信息,这可以通过ref解决。

    我们这里讲的属于后者,但是又有些特殊,特殊就在于子组件是个高阶组件,比如使用@connect @withRouter包裹过的组件(其实大部分组件都会被这两个包裹),具体示例如下:

    @withRouter
    export default class childComponent extends Component {
      constructor(props) {
        super(props);
        this.state = {};
      }
      render() {
        return (<div>this is childComponent</div>)
      }
    }
    @withRouter
    export default class parentComponent extends Component {
      constructor(props) {
        super(props);
        this.state = {}; 
      }
      render () {
        return <childComponent ref={(v) => { this.childCp = v; }}/>
      }
     }
    

      

    上面的childComponent被withRouter包裹过一遍后,这时候你在parentComponent中通过ref获取到的是并不会是childComponent,而是withRouter组件。这就比较尴尬了,我们大多数情况肯定是需要获取自己写的组件实例的。有一点需要讲明白:就是通过ref获取到的不是childComponent,在原理上是对的,如果获取到是childComponent组件那才是有问题的,有悖伦理知道哇。

    既然通过官方提供的ref无法获取到我们想要的ref,那我们就来仔细想下ref获取到的是啥?我们回归到javascript语言层面来看,那不就是组件中的this么。

    @withRouter
    export default class childComponent extends Component {
      constructor(props) {
        super(props);
        this.state = {}; // 我们需要获取到的就是这个this而已
      }
      render() {
        return (<div>this is childComponent</div>)
      }
    }
    

      

    知道我们需要获取到的是啥了,那就好办了,我给childComponent传一个prop专门来get这个this不就好了,比如使用getInstance

    @withRouter
    export default class childComponent extends Component {
      constructor(props) {
        super(props);
        this.state = {};
        const { getInstance } = props;
        if (typeof getInstance === 'function') {
          getInstance(this); // 在这里把this暴露给`parentComponent`
        }
      }
      render() {
        return (<div>this is childComponent</div>)
      }
    }
    @withRouter
    export default class parentComponent extends Component {
      constructor(props) {
        super(props);
        this.state = {}; 
      }
      render () {
        return (
         <childComponent 
           ref={(withRouter) => { this.childCpWrapper = withRouter; }}  // 这里获取的是`withRouter`组件,一般没啥用,这里写出来只是为了对比
           getInstance={(childCp) => { this.childCp = childCp; }} // 这里通过`getInstance`传一个回调函数接收`childComponent`实例即可
        />
        );
      }
     }
    

      

    perfect ! 问题解决了,这样我不管你怎么用啥高阶组件、用多少个高阶组件包裹我们childComponent,我们都可以通过一个getInstance,穿越千山万水直接获取childComponent实例。

    当然完美也是相对的,比如上面的方案中,我们得在每一个childComponent的构造函数中写那段暴露this的代码,麻烦、费劲。这时候我们可以写一个HOC专门来做这件事情,比如withRef:

    // 只做一件事,把`WrappedComponent`回传个`getInstance`(如果有的话)
    export default (WrappedComponent) => {
      return class withRef extends Component {
        static displayName = `withRef(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;
        render() {
          // 这里重新定义一个props的原因是:
          // 你直接去修改this.props.ref在react开发模式下会报错,不允许你去修改
          const props = {
            ...this.props,
          };
          // 在这里把getInstance赋值给ref,
          // 传给`WrappedComponent`,这样就getInstance能获取到`WrappedComponent`实例
          // 感谢评论区的[yangshenghaha]同学的完善
          props.ref = (el)=>{
              this.props.getInstance && this.props.getInstance(el);this.props.ref && this.props.ref(el);
          }
          return (
            <WrappedComponent {...props} />
          );
        }
      };
    };
    

      

    然后我们可以这样使用withRef

    @withRouter
    @withRef  // 这样使用是不是方便多了,注意:这句必须写在最接近`childComponent`的地方
    export default class childComponent extends Component {
      constructor(props) {
        super(props);
        this.state = {};
      }
      render() {
        return (<div>this is childComponent</div>)
      }
    }
    @withRouter
    export default class parentComponent extends Component {
      constructor(props) {
        super(props);
        this.state = {}; 
      }
      render () {
        return (
         <childComponent 
           // 这里获取的是`withRouter`组件,一般没啥用,这里写出来只是为了对比
           ref={(withRouter) => { this.childCpWrapper = withRouter; }}  
          // 这里通过`getInstance`传一个回调函数接收`childComponent`实例即可
           getInstance={(childCp) => { this.childCp = childCp; }} 
        />
        );
      }
     }
    

      

  • 相关阅读:
    一手遮天 Android
    一手遮天 Android
    一手遮天 Android
    一手遮天 Android
    一手遮天 Android
    一手遮天 Android
    一手遮天 Android
    一手遮天 Android
    一手遮天 Android
    一手遮天 Android
  • 原文地址:https://www.cnblogs.com/jcxfighting/p/10512005.html
Copyright © 2011-2022 走看看