在React.js中从父组件调用子组件函数

时间:2019-07-18 06:49:14

标签: javascript reactjs material-ui

我试图通过父组件中的按钮单击事件来调用子组件中的函数。

父组件:

class Parent extends Component{
    constructor(props){
        super(props);
        this.state = {
            //..
        }
    }

    handleSaveDialog = (handleSaveClick) => {
        this.handleSaveClick = handleSaveClick;
    }

    render(){
        return(
            <div>
                <Button onClick={this.openDialog}>Open Dialog</Button>
                <Dialog>
                    <DialogTitle id="form-dialog-title">Child Dialog</DialogTitle>
                    <DialogContent>
                        <Child handleSaveData={this.handleSaveDialog}/>
                    </DialogContent>
                    <DialogActions>
                        <Button onClick={this.handleSaveClick} color="primary">
                            Save
                        </Button>
                    </DialogActions>
                </Dialog>       
            </div>  
        );
    }
}

在上面的代码中,单击按钮后,父组件将呈现子组件模态对话框(基于Material-UI)。单击“保存”按钮,是“父项”中Dialog组件的一部分,应在Child组件中调用保存功能。如您所见,我已经通过名为handleSaveDialog的{​​{1}}组件道具传递了一个回调函数Child。一旦子组件安装并将回调传递给父组件,单击保存按钮将在子组件上调用handleSaveData

子组件:

handleSaveClick

在上面的代码中,我正在使用class Child extends Component{ constructor(props){ super(props); this.state = { //.. } } componentDidMount(){ console.log('mount'); this.props.handleSaveData( () => this.handleSaveClick()); } handleSaveClick = () => { console.log('save clicked'); } render(){ return( <div> //.. </div> ); } } 组件props传递的回调函数,并将其绑定到Parent组件的保存功能Child

问题:

当我第一次单击“父项”中的“打开对话框”按钮时,handleSaveClick会挂载“子项”组件。但是,单击Dialog按钮无效(没有错误)。关闭对话框之后,当我重新打开对话框并单击“保存”时,将触发“子”对话框中的Save,并在浏览器控制台中记录一条消息。知道为什么第二次而不是第一次有效吗? 请记住,只有当我单击父组件上的“打开对话框”时,才会装入/加载子组件。

参考文献:

https://material-ui.com/components/dialogs/#form-dialogs

Call child method from parent

https://github.com/kriasoft/react-starter-kit/issues/909#issuecomment-390556015

2 个答案:

答案 0 :(得分:2)

好吧,我不知道为什么会有这种情况,但是我想到的第一件事就是可以在Parent组件中编写handleSaveClick方法。并且如果您需要在子组件中可能发生的任何操作上使用该函数,则可以将该函数作为父组件的prop传递。这样您的两种情况都可以处理

  • 您可以在父组件中发生的事情上调用此方法
  • 您可以对子组件中发生的任何操作使用相同的方法。

如果您仍然认为必须在子组件中定义方法,则可以使用refs

答案 1 :(得分:1)

它将不起作用,因为如果您在this.handleSaveClick功能中管理日志render,则它将是undefined,因为没有重新渲染。因此,有两种方法可以实现:

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      open: false
    };
  }

  openDialog = () => {
    this.setState(preState => ({
      open: !preState.open
    }));
  };

  handleSaveDialog = handleSaveRef => {
    this.setState({
      handleSaveClick: handleSaveRef
    });
  };

  render() {
    console.log("Render", this.handleSaveClick);
    return (
      <div>
        <Button onClick={this.openDialog}>Open Dialog</Button>
        <Dialog open={this.state.open}>
          <DialogTitle id="form-dialog-title">Child Dialog</DialogTitle>
          <DialogContent>
            <Child handleSaveData={this.handleSaveDialog} />
          </DialogContent>
          <DialogActions>
            <Button onClick={this.state.handleSaveClick} color="primary">
              Save
            </Button>
          </DialogActions>
        </Dialog>
      </div>
    );
  }
class Child extends Component {

  componentDidMount() {
    console.log("mount");
    this.props.handleSaveData(this.handleSaveClick);
  }

  handleSaveClick = () => {
    console.log("save clicked");
  };

  render() {
    return <div>//..</div>;
  }
}

const childRef = React.createRef();
class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      open: false
    };
  }

  openDialog = () => {
    this.setState(preState => ({
      open: !preState.open
    }));
  };

  handleSaveClick = () => {
    if (childRef.current) {
      childRef.current.handleSaveClick();
    }
  };

  render() {
    return (
      <div>
        <Button onClick={this.openDialog}>Open Dialog</Button>
        <Dialog open={this.state.open}>
          <DialogTitle id="form-dialog-title">Child Dialog</DialogTitle>
          <DialogContent>
            <Child ref={childRef} />
          </DialogContent>
          <DialogActions>
            <Button onClick={this.handleSaveClick} color="primary">
              Save
            </Button>
          </DialogActions>
        </Dialog>
      </div>
    );
  }
}
class Child extends Component {
  handleSaveClick = () => {
    console.log("save clicked");
  };

  render() {
    return <div>//..</div>;
  }
}

  • 使用回调函数保存实例,然后使用箭头功能:
class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      open: false
    };
  }

  openDialog = () => {
    this.setState(preState => ({
      open: !preState.open
    }));
  };

  handleSaveDialog = handleSaveRef => {
    this.handleSaveClick = handleSaveRef;
  };

  render() {
    return (
      <div>
        <Button onClick={this.openDialog}>Open Dialog</Button>
        <Dialog open={this.state.open}>
          <DialogTitle id="form-dialog-title">Child Dialog</DialogTitle>
          <DialogContent>
            <Child handleSaveData={this.handleSaveDialog} />
          </DialogContent>
          <DialogActions>
            <Button onClick={() => this.handleSaveClick()} color="primary">
              Save
            </Button>
          </DialogActions>
        </Dialog>
      </div>
    );
  }
}
class Child extends Component {
  componentDidMount() {
    console.log("mount");
    this.props.handleSaveData(this.handleSaveClick);
  }

  handleSaveClick = () => {
    console.log("save clicked");
  };

  render() {
    return <div>//..</div>;
  }
}

您将需要在onClick中使用箭头函数,因为每次单击鼠标键都会创建一个新函数,从而获得handleClick的新实例。而且,如果您通过this.handleClick,则将无法使用,因为它是undefined。您可以通过在this.handleClick函数中记录render的值来进行检查。


注意:使用2选项更可靠。

希望这会有所帮助!