我有两个组成部分:

父组件 子组件

我试图从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])
}

其他回答

如果你这样做只是因为你想让Child为它的父对象提供一个可重用的trait,那么你可以考虑使用render-props来代替。

这种技术实际上把结构颠倒过来了。Child现在包装了父对象,所以我将它重命名为AlertTrait。为了保持连续性,我保留了Parent这个名字,尽管它现在已经不是一个真正的Parent了。

// Use it like this:

  <AlertTrait renderComponent={Parent}/>


class AlertTrait extends Component {
  // You will need to bind this function, if it uses 'this'
  doAlert() {
    alert('clicked');
  }
  render() {
    return this.props.renderComponent({ doAlert: this.doAlert });
  }
}

class Parent extends Component {
  render() {
    return (
      <button onClick={this.props.doAlert}>Click</button>
    );
  }
}

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

Parent接收doAlert作为道具,并在需要时调用它。

(为了清晰起见,我在上面的例子中调用道具renderComponent。但在上面链接的React文档中,他们只称之为渲染。)

Trait组件可以在它的渲染函数中渲染父元素周围的东西,但它不渲染父元素内部的任何东西。实际上,如果它将另一个道具(例如renderChild)传递给父对象,父对象就可以在它的渲染方法中使用它,它就可以在父对象内部渲染东西。

这与OP要求的有些不同,但有些人可能会在这里结束(就像我们所做的那样),因为他们想要创建一个可重用的trait,并且认为子组件是实现这一点的好方法。

我认为调用方法的最基本方法是在子组件上设置请求。然后,一旦子进程处理了请求,它就调用一个回调方法来重置请求。

为了能够多次发送相同的请求,重置机制是必要的。

在父组件中

在父类的渲染方法中:

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

父节点需要两个方法,在两个方向上与子节点进行通信。

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

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

我们将创建一个父元素,它将呈现一个<Child/>组件。如您所见,对于将要呈现的组件,您需要添加ref属性并为其提供名称。 然后,位于父类中的triggerChildAlert函数将访问this context的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>
        );
    }
}

这里是源代码- 希望对你有所帮助!

对于功能组件,最简单的方法是

父组件

parent.tsx

import React, { useEffect, useState, useRef } from "react";
import child from "../../child"

const parent: React.FunctionComponent = () => {
  const childRef: any = useRef();
}

const onDropDownChange: any = (event): void => {
    const target = event.target;
    childRef.current.onFilterChange(target.value);
};

return <child ref={childRef} />

export default parent;

子组件

child.tsx

import React, {   useState,   useEffect,   forwardRef,   useRef,   useImperativeHandle, } from "react";

const Child = forwardRef((props, ref) => {
 useImperativeHandle(ref, () => ({
    onFilterChange(id) {
      console.log("Value from parent", id)
    },
  }));
})

Child.displayName = "Child";

export default Child;

这里有个虫子?注意: 我同意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