从父级调用子级方法

。_饼干妹妹 提交于 2020-03-02 16:56:09

我有两个组成部分。

  1. 父组件
  2. 子组件

我试图从父级调用孩子的方法,我尝试过这种方法,但没有得到结果

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>
    );
  }
}

有没有一种方法可以从父级调用子级的方法?

注意:子组件和父组件位于两个不同的文件中


#1楼

首先,让我表示,这通常不是在React领域中解决问题的方法。 通常,您要做的是将功能传递给props中的子代,并传递事件中子代的通知(或者更好的是: dispatch )。

但是,如果必须在子组件上公开命令式方法,则可以使用refs 。 请记住,这是一个逃生舱口,通常表示可以使用更好的设计。

以前,仅基于类的组件才支持引用。 随着React Hooks的出现,情况不再如此

使用挂钩和功能组件( >= react@16.8

const { forwardRef, useRef, useImperativeHandle } = 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 useImperativeHandle(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> ); }; ReactDOM.render( <Parent />, document.getElementById('root') );
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script> <div id="root"></div>

useImperativeHandle()文档在这里

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

使用类组件( >= react@16.4

const { Component } = React; 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'));
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script> <div id="root"></div>

旧版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>


#2楼

https://facebook.github.io/react/tips/expose-component-functions.html有关更多答案的参考,请参见此处

通过查看“原因”组件的引用,您正在破坏封装并使您无法仔细检查组件的所有使用位置就无法重构该组件。 因此,我们强烈建议将ref视为组件的私有属性,就像state一样。

通常,数据应通过道具传递到树下。 有一些例外(例如,调用.focus()或触发一次不会真正“改变”状态的一次性动画),但是每当您公开称为“ set”的方法时,道具通常更好的选择。 尝试使其内部输入组件担心其大小和外观,以便其祖先都不做。


#3楼

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

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方法。 这样,当您单击父级中的按钮时,它将调用clickChild ,后者将调用子级的getAlert

如果您的孩子用connect()包裹起来,那么这也可以工作,因此您不需要getWrappedInstance() hack。

请注意,您不能在父级中使用onClick={this.clickChild} ,因为在渲染父级时未安装子级,因此尚未分配this.clickChild 。 使用onClick={() => this.clickChild()}很好,因为单击按钮时this.clickChild应该已经被分配了。


#4楼

您可以进行继承反转(在此处查找: https : //medium.com/@franleplant/react-higher-order-components-in-depth-cf9032ee6c3e )。 这样,您就可以访问要包装的组件的实例(因此您将可以访问其功能)


#5楼

如果只是因为您希望子项为其父项提供可重用的特征而这样做,则可以考虑使用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道具中给定的任何组件。

父级收到doAlert作为道具,并可以在需要时调用它。

(为清楚起见,我在上面的示例中称为prop renderComponent 。但是在上面链接的React docs中,他们只是将其称为render 。)

Trait组件可以在其render函数中渲染围绕Parent的内容,但不会渲染Parent内部的任何内容。 实际上,如果它向父级传递了另一个道具(例如renderChild ),则它可以在父级内部渲染事物,然后父级可以在其render方法中使用它。

这与OP要求的有所不同,但有些人可能会像我们一样最终来到这里(因为我们这样做了),因为他们想创建可重用的特征,并认为子组件是实现此目标的好方法。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!