我有两个组成部分:

父组件 子组件

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

是否有一种方法从父调用子方法?

注意:子组件和父组件在两个不同的文件中。


当前回答

这个模式类似于@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>;
};

其他回答

你可以在这里使用另一个模式:

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方法。这样,当你点击parent中的按钮时,它会调用clickChild,它会调用child的getAlert。

如果你的子文件是用connect()包装的,那么你就不需要getWrappedInstance()了。

注意你不能使用onClick={this。在父元素中使用clickChild},因为当父元素被渲染时,子元素不会被挂载,所以这个。clickChild还没有被分配。使用onClick={() => this. clickchild()}是好的,因为当你点击按钮this. clickchild()}。clickChild应该已经被分配了。

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

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

父组件

import Child from './Child'

export default function Parent(props) {
    const [childRefreshFunction, setChildRefreshFunction] = useState(null);

    return (
        <div>
            <button type="button" onClick={() => {
                childRefreshFunction();
            }}>Refresh child</button>
            <Child setRefreshFunction={(f) => {
                setChildRefreshFunction(f);
            }} />
        </div>
    )
}

子组件

export default function Child(props) {

    useEffect(() => {
        props.setRefreshFunction(() => refreshMe);
    }, []);

    function refreshMe() {
        fetch('http://example.com/data.json')....
    };

    return (
        <div>
            child
        </div>
    )
}

我们很高兴有一个自定义钩子,我们称之为useCounterKey。它只是设置了一个反键,或者一个从0开始计数的键。它返回的函数将重置键(即增量)。(我相信这是React中最常用的重置组件的方法-只需撞击键。)

然而,这个钩子也适用于任何情况下,你想发送一个一次性的消息给客户端做某事。例如,我们用它来聚焦子控件中的某个父事件——它只是在键更新时自动聚焦。(如果需要更多道具,它们可以在重置按键之前设置,以便在事件发生时可用。)

这个方法有一点学习曲线b/c,它不像典型的事件处理程序那么直接,但它似乎是我们在React中发现的最习惯的处理方法(因为键已经以这种方式起作用)。Def开放对这种方法的反馈,但它是工作得很好!

// Main helper hook:
export function useCounterKey() {
  const [key, setKey] = useState(0);
  return [key, () => setKey(prev => prev + 1)] as const;
}

示例用法:

// Sample 1 - normal React, just reset a control by changing Key on demand
function Sample1() {
  const [inputLineCounterKey, resetInputLine] = useCounterKey();

  return <>
    <InputLine key={inputLineCounterKey} />
    <button onClick={() => resetInputLine()} />
  <>;
}

// Second sample - anytime the counterKey is incremented, child calls focus() on the input
function Sample2() {
  const [amountFocusCounterKey, focusAmountInput] = useCounterKey();

  // ... call focusAmountInput in some hook or event handler as needed

  return <WorkoutAmountInput focusCounterKey={amountFocusCounterKey} />
}

function WorkoutAmountInput(props) {
  useEffect(() => {
    if (counterKey > 0) {
      // Don't focus initially
      focusAmount();
    }
  }, [counterKey]);

  // ...
}

(counterKey的概念要归功于Kent Dodds。)

如果你这样做只是因为你想让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,并且认为子组件是实现这一点的好方法。