首页 文章

从父母调用子方法

提问于
浏览
207

我有两个组成部分 .

  • 父组件

  • 子组件

我试图从Parent调用child的方法,我尝试了这种方式,但无法得到结果

class Parent extends Component {
  render() {
    return (
      <Child>
        <button onClick={Child.getAlert()}>Click</button>
      </Child>
      );
    }
  }

class Child extends Component {
  getAlert() {
    alert('clicked');
  }

  render() {
    return (
      <h1 ref="hello">Hello</h1>
    );
  }
}

有没有办法从父母那里调用孩子的方法?

注意:Child和Parent组件位于两个不同的文件中

7 回答

  • 22

    你可以用refs

    以前,refs仅支持基于类的组件 . 随着React Hooks的出现,情况已不再如此

    使用挂钩和功能组件

    import React, { forwardRef, useRef, useImperativeMethods } from 'react';
    
    // We need to wrap component in `forwardRef` in order to gain
    // access to the ref object that is assigned using the `ref` prop.
    // This ref is passed as the second parameter to the function component.
    const Child = forwardRef((props, ref) => {
    
      // The component instance will be extended
      // with whatever you return from the callback passed 
      // as the second argument 
      useImperativeMethods(ref, () => ({
    
        getAlert() {
          alert("getAlert from Child");
        }
    
      }));
    
      return <h1>Hi</h1>;
    });
    
    const Parent = () => {
      // In order to gain access to the child component instance,
      // you need to assign it to a `ref`, so we call `useRef()` to get one
      const childRef = useRef();
    
      return (
        <div>
          <Child ref={childRef} />
          <button onClick={() => childRef.current.getAlert()}>Click</button>
        </div>
      );
    };
    

    Functioning example

    useImperativeMethods() 的文档是here

    useImperativeMethods自定义使用ref时公开给父组件的实例值 .

    使用类组件

    class Parent extends Component {
      constructor(props) {
        super(props);
        this.child = React.createRef();
      }
    
      onClick = () => {
        this.child.current.getAlert();
      };
    
      render() {
        return (
          <div>
            <Child ref={this.child} />
            <button onClick={this.onClick}>Click</button>
          </div>
        );
      }
    }
    
    class Child extends Component {
      getAlert() {
        alert('getAlert from Child');
      }
    
      render() {
        return <h1>Hello</h1>;
      }
    }
    
    ReactDOM.render(<Parent />, document.getElementById('root'));
    

    Functioning Example

    旧版API(React <16.3)

    出于历史目的,这里是您在16.3之前使用React版本的基于回调的样式:

    const { Component } = React;
    const { render } = ReactDOM;
    
    class Parent extends Component {
      render() {
        return (
          <div>
            <Child ref={instance => { this.child = instance; }} />
            <button onClick={() => { this.child.getAlert(); }}>Click</button>
          </div>
        );
      }
    }
    
    class Child extends Component {
      getAlert() {
        alert('clicked');
      }
    
      render() {
        return (
          <h1>Hello</h1>
        );
      }
    }
    
    
    render(
      <Parent />,
      document.getElementById('app')
    );
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    
    <div id="app"></div>
    
  • 287

    您可以在此处使用其他模式:

    class Parent extends Component {
     render() {
      return (
        <div>
          <Child setClick={click => this.clickChild = click}/>
          <button onClick={() => this.clickChild()}>Click</button>
        </div>
      );
     }
    }
    
    class Child extends Component {
     constructor(props) {
        super(props);
        this.getAlert = this.getAlert.bind(this);
     }
     componentDidMount() {
        this.props.setClick(this.getAlert);
     }
     getAlert() {
        alert('clicked');
     }
     render() {
      return (
        <h1 ref="hello">Hello</h1>
      );
     }
    }
    

    它的作用是在挂载子进程时设置父进程的clickChild方法 . 这样,当您单击父级中的按钮时,它将调用clickChild,该单元调用child的getAlert .

    如果您的孩子用connect()包装,这也适用,因此您不需要getWrappedInstance()hack .

    请注意,您不能在父级中使用onClick = ,因为在呈现父级时未挂载子级,因此尚未分配this.clickChild . 使用onClick = {()=> this.clickChild()}很好,因为当您单击按钮时,应该已经分配了this.clickChild .

  • 0

    https://facebook.github.io/react/tips/expose-component-functions.html更多答案ref这里Call methods on React children components

    通过查看“原因”组件的引用,您将破坏封装并使得无法在不仔细检查其使用的所有位置的情况下重构该组件 . 因此,我们强烈建议将refs视为组件的私有,就像状态一样 .

    通常,数据应该通过props传递到树下 . 这有一些例外(例如调用.focus()或触发一次性动画并没有真正“改变”状态)但是当你暴露一个名为“set”的方法时,通常会有道具一个更好的选择 . 尝试做到这一点,以便内部输入组件担心它的大小和外观,以便它的祖先没有 .

  • 0

    如果您这样做只是因为您希望Child为其父母提供可重复使用的特征,那么您可以考虑改为使用using render-props .

    这种技术实际上颠倒了结构 . Child 现在包装了父级,所以我将其重命名为 AlertTrait . 我保持名称 Parent 的连续性,虽然它现在不是真正的父母 .

    // Use it like this:
    
      <AlertTrait renderComponent={Parent}/>
    
    
    class AlertTrait extends Component {
      // You may need to bind this function, if it is stateful
      doAlert() {
        alert('clicked');
      }
      render() {
        return this.props.renderComponent(this.doAlert);
      }
    }
    
    class Parent extends Component {
      render() {
        return (
          <button onClick={this.props.doAlert}>Click</button>
        );
      }
    }
    

    在这种情况下,AlertTrait提供了一个或多个特征,它作为道具传递给它在 renderComponent 道具中给出的任何组件 .

    Parent收到 doAlert 作为道具,可以在需要时调用它 .

    (为清楚起见,我在上面的示例中调用了道具 renderComponent . 但在上面链接的React文档中,他们只是将其称为 render . )

    Trait组件可以在其渲染函数中渲染Parent周围的东西,但它不会在父级内部渲染任何内容 . 实际上它可以在Parent内部渲染内容,如果它将另一个prop(例如 renderChild )传递给父级,父级可以在其渲染方法中使用它 .

    这与OP要求的有些不同,但是有些人可能会在这里结束(就像我们一样),因为他们想要创建一个可重用的特性,并认为子组件是一个很好的方法 .

  • 59

    我们可以用另一种方式使用refs-

    我们将创建一个Parent元素,它将呈现一个 <Child/> 组件 . 如您所见,要呈现的组件,您需要添加ref属性并为其提供名称 .
    然后,位于父类中的 triggerChildAlert 函数将访问此上下文的refs属性(当触发 triggerChildAlert 函数时,将访问子引用,它将具有子元素的所有函数) .

    class Parent extends React.Component {
        triggerChildAlert(){
            this.refs.child.callChildMethod();
            // to get child parent returned  value-
            // this.value = this.refs.child.callChildMethod();
            // alert('Returned value- '+this.value);
        }
    
        render() {
            return (
                <div>
                    {/* Note that you need to give a value to the ref parameter, in this case child*/}
                    <Child ref="child" />
                    <button onClick={this.triggerChildAlert}>Click</button>
                </div>
            );
        }
    }
    

    现在,理论上设计的子组件看起来像:

    class Child extends React.Component {
        callChildMethod() {
            alert('Hello World');
            // to return some value
            // return this.state.someValue;
        }
    
        render() {
            return (
                <h1>Hello</h1>
            );
        }
    }
    

    这是source code-
    希望能帮到你!

  • 1

    您可以进行继承反转(在此查找:https://medium.com/@franleplant/react-higher-order-components-in-depth-cf9032ee6c3e) . 这样你就可以访问你将要包装的组件的实例(因此你'll be able to access it'的函数)

  • 2

    我认为调用方法的最基本方法是设置请求儿童组成部分 . 然后,只要子处理请求,它就会调用回调方法来重置请求 .

    重置机制必须能够在彼此之后多次发送相同的请求 .

    在父组件中

    在父级的render方法中:

    const { request } = this.state;
    return (<Child request={request} onRequestHandled={()->resetRequest()}/>);
    

    父母需要2种方法,与孩子在2个方向上进行交流 .

    sendRequest() {
      const request = { param: "value" };
      this.setState({ request });
    }
    
    resetRequest() {
      const request = null;
      this.setState({ request });
    }
    

    在子组件中

    孩子更新其内部状态,从道具复制请求 .

    constructor(props) {
      super(props);
      const { request } = props;
      this.state = { request };
    }
    
    static getDerivedStateFromProps(props, state) {
      const { request } = props;
      if (request !== state.request ) return { request };
      return null;
    }
    

    然后它最终处理请求,并将重置发送给父:

    componentDidMount() {
      const { request } = this.state;
      // todo handle request.
    
      const { onRequestHandled } = this.props;
      if (onRequestHandled != null) onRequestHandled();
    }
    

相关问题