我有两个组成部分:

父组件 子组件

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

其他回答

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

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

在父组件中

在父类的渲染方法中:

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

你可以很容易地使用你的子组件作为react自定义钩子来应用这个逻辑。

如何实施?

子函数返回一个函数。 子函数返回JSON:{函数、HTML或其他值}作为示例。

在这个例子中,应用这个逻辑没有意义,但很容易看出:

const {useState} = React; //Parent const Parent = () => { //custome hook const child = useChild(); return ( <div> {child.display} <button onClick={child.alert}> Parent call child </button> {child.btn} </div> ); }; //Child const useChild = () => { const [clickCount, setClick] = React.useState(0); {/* child button*/} const btn = ( <button onClick={() => { setClick(clickCount + 1); }} > Click me </button> ); return { btn: btn, //function called from parent alert: () => { alert("You clicked " + clickCount + " times"); }, display: <h1>{clickCount}</h1> }; }; const rootElement = document.getElementById("root"); ReactDOM.render(<Parent />, rootElement); <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script> <div id="root"></div>

父组件

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。)

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

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