我有两个组成部分:

父组件 子组件

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

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

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


当前回答

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

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

在父组件中

在父类的渲染方法中:

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

其他回答

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

在这里,我将给出四种可能的组合:

类父类|钩子子类 钩子父类|类子类 钩父钩子 类父类|类子类

1. 类父类|钩子子类

class Parent extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef()
  }

  render() {
    return (<View>
      <Child ref={this.myRef}/>
      <Button title={'call me'}
              onPress={() => this.myRef.current.childMethod()}/>
    </View>)
  }
}

const Child = React.forwardRef((props, ref) => {

  useImperativeHandle(ref, () => ({
    childMethod() {
      childMethod()
    }
  }))

  function childMethod() {
    console.log('call me')
  }

  return (<View><Text> I am a child</Text></View>)
})

2. 钩子父类|类子类

function Parent(props) {

  const myRef = useRef()

  return (<View>
    <Child ref={myRef}/>
    <Button title={'call me'}
            onPress={() => myRef.current.childMethod()}/>
  </View>)
}

class Child extends React.Component {

  childMethod() {
    console.log('call me')
  }

  render() {
    return (<View><Text> I am a child</Text></View>)
  }
}

3.钩父钩子

function Parent(props) {

  const myRef = useRef()

  return (<View>
    <Child ref={myRef}/>
    <Button title={'call me'}
            onPress={() => myRef.current.childMethod()}/>
  </View>)
}

const Child = React.forwardRef((props, ref) => {

  useImperativeHandle(ref, () => ({
    childMethod() {
      childMethod()
    }
  }))

  function childMethod() {
    console.log('call me')
  }

  return (<View><Text> I am a child</Text></View>)
})

4. 类父类|类子类

class Parent extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef()
  }

  render() {
    return (<View>
      <Child ref={this.myRef}/>
      <Button title={'call me'}
              onPress={() => this.myRef.current.childMethod()}/>
    </View>)
  }
}

class Child extends React.Component {

  childMethod() {
    console.log('call me')
  }

  render() {
    return (<View><Text> I am a child</Text></View>)
  }
}

首先,让我声明一下,在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>

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

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

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

我们可以用另一种方式使用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>
        );
    }
}

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