我有两个组成部分:

父组件 子组件

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

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

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


当前回答

I wasn't satisfied with any of the solutions presented here. There is actually a very simple solution that can be done using pure Javascript without relying upon some React functionality other than the basic props object - and it gives you the benefit of communicating in either direction (parent -> child, child -> parent). You need to pass an object from the parent component to the child component. This object is what I refer to as a "bi-directional reference" or biRef for short. Basically, the object contains a reference to methods in the parent that the parent wants to expose. And the child component attaches methods to the object that the parent can call. Something like this:

// Parent component.
function MyParentComponent(props) {

   function someParentFunction() {
      // The child component can call this function.
   }

   function onButtonClick() {
       // Call the function inside the child component.
       biRef.someChildFunction();
   }

   // Add all the functions here that the child can call.
   var biRef = {
      someParentFunction: someParentFunction
   }

   return <div>
       <MyChildComponent biRef={biRef} />
       <Button onClick={onButtonClick} />
   </div>;
}


// Child component
function MyChildComponent(props) {

   function someChildFunction() {
      // The parent component can call this function.
   }


   function onButtonClick() {
      // Call the parent function.
      props.biRef.someParentFunction();
   }

   // Add all the child functions to props.biRef that you want the parent
   // to be able to call.
   props.biRef.someChildFunction = someChildFunction;

   return <div>
       <Button onClick={onButtonClick} />
   </div>;
}

这种解决方案的另一个优点是,您可以在父节点和子节点中添加更多的函数,同时只使用一个属性将它们从父节点传递给子节点。

对上述代码的改进是不直接向biRef对象添加父函数和子函数,而是向子成员添加。父函数应该添加到名为“Parent”的成员中,而子函数应该添加到名为“child”的成员中。

// Parent component.
function MyParentComponent(props) {

   function someParentFunction() {
      // The child component can call this function.
   }

   function onButtonClick() {
       // Call the function inside the child component.
       biRef.child.someChildFunction();
   }

   // Add all the functions here that the child can call.
   var biRef = {
      parent: {
          someParentFunction: someParentFunction
      }
   }

   return <div>
       <MyChildComponent biRef={biRef} />
       <Button onClick={onButtonClick} />
   </div>;
}


// Child component
function MyChildComponent(props) {

   function someChildFunction() {
      // The parent component can call this function.
   }


   function onButtonClick() {
      // Call the parent function.
      props.biRef.parent.someParentFunction();
   }

   // Add all the child functions to props.biRef that you want the parent
   // to be able to call.
   props.biRef {
       child: {
            someChildFunction: someChildFunction
       }
   }

   return <div>
       <Button onClick={onButtonClick} />
   </div>;
}

通过将父函数和子函数放在biRef对象的独立成员中,您将在两者之间有一个清晰的分离,并且很容易看到哪个属于父函数或子函数。如果父组件和子组件中都出现了相同的函数,它还有助于防止子组件意外地覆盖父函数。

One last thing is that if you note, the parent component creates the biRef object with var whereas the child component accesses it through the props object. It might be tempting to not define the biRef object in the parent and access it from its parent through its own props parameter (which might be the case in a hierarchy of UI elements). This is risky because the child may think a function it is calling on the parent belongs to the parent when it might actually belong to a grandparent. There's nothing wrong with this as long as you are aware of it. Unless you have a reason for supporting some hierarchy beyond a parent/child relationship, it's best to create the biRef in your parent component.

其他回答

我尝试使用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");
  };
}

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

https://facebook.github.io/react/tips/expose-component-functions.html 更多答案请参考这里调用React子组件上的方法

通过查看“reason”组件的引用,您正在破坏封装,如果不仔细检查使用该组件的所有位置,就不可能重构该组件。正因为如此,我们强烈建议将引用视为组件的私有,就像状态一样。

一般来说,数据应该通过道具沿着树向下传递。有一些例外情况(比如调用.focus()或触发一个不会真正“改变”状态的一次性动画),但任何时候你要公开一个名为“set”的方法,道具通常是更好的选择。试着让内部输入组件担心它的大小和外观,这样它的祖先就不会担心。

我使用useEffect钩子来克服做这些事情的头痛,所以现在我传递一个变量给child,像这样:

import { useEffect, useState } from "react"; export const ParentComponent = () => { const [trigger, setTrigger] = useState(false); return ( <div onClick={() => { setTrigger(trigger => !trigger); }}> <ChildComponent trigger={trigger}></ChildComponent> </div> ); }; export const ChildComponent = (props) => { const triggerInvokedFromParent = () => { console.log('TriggerInvokedFromParent'); }; useEffect(() => { triggerInvokedFromParent(); }, [props.trigger]); return <span>ChildComponent</span>; };

这里是我的演示:https://stackblitz.com/edit/react-dgz1ee?file=styles.css

I am using useEffect to call the children component's methods. I have tried with Proxy and Setter_Getter but sor far useEffect seems to be the more convenient way to call a child method from parent. To use Proxy and Setter_Getter it seems there is some subtlety to overcome first, because the element firstly rendered is an objectLike's element through the ref.current return => <div/>'s specificity. Concerning useEffect, you can also leverage on this approach to set the parent's state depending on what you want to do with the children.

在我提供的演示链接中,你会发现我完整的ReactJS代码和我的草稿,所以你可以欣赏我的解决方案的工作流程。

在这里,我只提供了相关代码的ReactJS片段。:

import React, {
  Component,
  createRef,
  forwardRef,
  useState,
  useEffect
} from "react"; 

{...}

// Child component
// I am defining here a forwardRef's element to get the Child's methods from the parent
// through the ref's element.
let Child = forwardRef((props, ref) => {
  // I am fetching the parent's method here
  // that allows me to connect the parent and the child's components
  let { validateChildren } = props;
  // I am initializing the state of the children
  // good if we can even leverage on the functional children's state
  let initialState = {
    one: "hello world",
    two: () => {
      console.log("I am accessing child method from parent :].");
      return "child method achieve";
    }
  };
  // useState initialization
  const [componentState, setComponentState] = useState(initialState);
  // useEffect will allow me to communicate with the parent
  // through a lifecycle data flow
  useEffect(() => {
    ref.current = { componentState };
    validateChildren(ref.current.componentState.two);
  });

{...}

});

{...}

// Parent component
class App extends Component {
  // initialize the ref inside the constructor element
  constructor(props) {
    super(props);
    this.childRef = createRef();
  }

  // I am implementing a parent's method
  // in child useEffect's method
  validateChildren = childrenMethod => {
    // access children method from parent
    childrenMethod();
    // or signaling children is ready
    console.log("children active");
  };

{...}
render(){
       return (
          {
            // I am referencing the children
            // also I am implementing the parent logic connector's function
            // in the child, here => this.validateChildren's function
          }
          <Child ref={this.childRef} validateChildren={this.validateChildren} />
        </div>
       )
}

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

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