【问题标题】:Calling Child component function from Parent component in React.js从 React.js 中的父组件调用子组件函数
【发布时间】:2019-11-27 00:55:56
【问题描述】:

我正在尝试从父组件中的按钮单击事件调用子组件中的函数。

父组件:

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)。保存按钮是 Parent 中 Dialog 组件的一部分,单击时应调用 Child 组件中的保存函数。如您所见,我通过名为handleSaveDataChildcomponent 属性传递了一个回调函数handleSaveDialog。一旦子组件挂载并将回调传递给父组件,保存按钮单击将在子组件上调用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>      

        );
    }
}

在上面的代码中,我使用的是访问Parent组件props传递的回调函数并将其绑定到Child组件的保存函数handleSaveClick

问题:

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

参考资料:

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

Call child method from parent

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

【问题讨论】:

  • 你能把你的代码放在codesandbox或stackblitz之类的地方吗?从这里很难说。

标签: javascript reactjs material-ui


【解决方案1】:

它不起作用,因为如果您在render 函数中控制台记录this.handleSaveClick,它将是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。您可以通过在render 函数中记录this.handleClick 的值来检查这一点。


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

希望这会有所帮助!

【讨论】:

  • 作为警告,您可能会被否决。您应该发布代码而不是链接,因为它们可能会消失:)
  • 有趣!我不知道你可以使用这样的 refs。赞成。
  • 我不想使用 refs。这对我来说看起来更好:github.com/kriasoft/react-starter-kit/issues/…
  • 已更新答案以使用此方法。检查第三点。
  • 我注意到一件事,createRef 被放置在课堂之外。如果放在构造函数中或类中的某个地方,为什么它不起作用?这有什么缺点吗?
【解决方案2】:

好吧,我不知道你为什么会有这种情况,但我首先想到的是你可以在父组件中编写handleSaveClick 方法。如果您需要在子组件中可能发生的任何操作上使用该函数,您可以将此函数作为来自父组件的 prop 传递。所以你的两种情况都可以处理

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

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

【讨论】:

  • 在保存数据时实现了很多逻辑,包括验证。我更喜欢将它保留在有意义的子组件中。
  • 然后如前所述,您可以在此特定场景中使用refs
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-16
  • 1970-01-01
  • 2020-03-25
  • 1970-01-01
  • 2017-12-18
相关资源
最近更新 更多