我有两个组成部分:

父组件 子组件

我试图从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或ref创建一个函数。

我更喜欢父用子的创建功能。有很多种方法。

使用功能组件时

在父

function Parent(){
  const [functionToCall, createFunctionToCall] = useState(()=>()=>{})

  return (
   <Child createFunctionToCall={createFunctionToCall} />
  )
}

在儿童

function Child({createFunctionToCall}){
  useEffect(()=>{
    function theFunctionToCall(){
      // do something like setting something
      // don't forget to set dependancies properly.
    }
    createFunctionToCall(()=>theFunctionToCall)
  },[createFunctionToCall])
}

其他回答

我希望我没有重复上面的内容但是如果传递一个回调道具来设置父类中的函数呢?这很有效,也很简单。(添加的代码在////之间)

class Parent extends Component {
  ///// 
  getAlert = () => {} // initial value for getAlert

  setGetAlertMethod = (newMethod) => {
    this.getAlert = newMethod;
  }
  /////

  render() {
    return (
      <Child setGetAlertMethod={this.setGetAlertMethod}>
        <button onClick={this.getAlert}>Click</button>
      </Child>
      );
    }
  }



class Child extends Component {
  /////
  componentDidMount() {
    this.props.setGetAlertMethod(this.getAlert);
  }
  /////

  getAlert() => {
    alert('clicked');
  }

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

我尝试使用createRef或useRef。它们都返回null。

其次,这个答案提出通过一个道具来设置一个在我看来最合理的函数。但如果子组件用于多个地方,则应该将额外的道具添加到其他地方。此外,如果您想在孙辈中调用一个方法,这个方法可能太啰嗦或太冗长。

所以我用一种非常原始的方式创建了我自己的函数存储。

下面是functionStore.js文件

const fns = {};

export function setFn(componentName, fnName, fn) {
  if (fns[componentName]) {
    fns[componentName][fnName] = fn;
  } else {
    fns[componentName] = { fnName: fn };
  }
}

export function callFn(componentName, fnName) {
  fns[componentName][fnName]();
}

我只是设置了需要从任何组件调用的函数。

import { setFn } from "./functionStore";
export class AComponent extends React.Component {
  componentDidMount() {
    setFn("AComponent", "aFunc", this.aFunc);
  }
  aFunc = () => { console.log("aFunc is called!"); };
}

然后我从其他组件调用它

import { callFn } from "./functionStore";
export class BComponent extends React.Component {
  
  // just call the function
  bFunc = () => { 
    callFn("AComponent", "aFunc");
  };
}

一个缺点是要调用的函数应该是无参数的。但这也可能以某种方式解决。目前,我不需要传递参数。

这里有个虫子?注意: 我同意rossipedia的解决方案使用forwardRef, useRef, useImperativeHandle

网上有一些错误的信息说refs只能从React类组件创建,但如果你使用上面提到的钩子,你确实可以使用函数组件。注意,只有在我将文件更改为在导出组件时不使用withRouter()后,钩子才对我有效。例如,从

export default withRouter(TableConfig);

取而代之的是

export default TableConfig;

In hindsight the withRouter() is not needed for such a component anyway, but usually it doesn't hurt anything having it in. My use case is that I created a component to create a Table to handle the viewing and editing of config values, and I wanted to be able to tell this Child component to reset it's state values whenever the Parent form's Reset button was hit. UseRef() wouldn't properly get the ref or ref.current (kept on getting null) until I removed withRouter() from the file containing my child component TableConfig

这个模式类似于@brickingup answer。但是在这个版本中,你可以设置任意多的子动作。

import { useEffect } from "react";

export const Parent = () => {
  const childEvents = { click: () => {} };

  return (
    <div onClick={() => childEvents.click()}>
      <Child events={childEvents}></Child>
    </div>
  );
};

export const Child = (props) => {
  const click = () => {
    alert("click from child");
  };

  useEffect(() => {
    if (props.events) {
      props.events.click = click;
    }
  }, []);

  return <span>Child Component</span>;
};

首先,让我声明一下,在React领域,这通常不是做事的方式。通常,您要做的是将功能传递给道具中的子元素,并在事件中传递子元素的通知(或者更好的方法是:分派)。

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

以前,引用只支持基于类的组件。 随着React Hooks的出现,这种情况不再存在

Modern React with Hooks (v16.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时暴露给父组件的实例值。

使用类组件的遗留API (>= 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中不太常见:

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>