I am trying to call a function in my child ponent from a button click event in my parent ponent.
Parent Component:
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>
);
}
}
In the above code, the Parent ponent renders a Child ponent modal dialog (based on Material-UI) on click of a button. The Save button, part of the Dialog
ponent in Parent, when clicked should call a save function in the Child
ponent. As you can see, I have passed a callback function handleSaveDialog
through the Child
ponent props named handleSaveData
. The save button click will call handleSaveClick
on the child, once the Child ponent mounts and passes the callback to the Parent ponent.
Child Component:
class Child extends Component{
constructor(props){
super(props);
this.state = {
//..
}
}
ponentDidMount(){
console.log('mount');
this.props.handleSaveData( () => this.handleSaveClick());
}
handleSaveClick = () => {
console.log('save clicked');
}
render(){
return(
<div>
//..
</div>
);
}
}
In the above code, I am using the accessing the callback function passed by the Parent
ponent props and binding it to the Child
ponent's save fucntion handleSaveClick
.
Problem:
When I click the Open Dialog button in Parent, for the first time, the Dialog
mounts the Child ponent. However, the click on Save
button does not work (no error). After, closing the dialog, when I reopen the Dialog and click on Save, the handleSaveClick
in the Child dialog is triggered and a message is logged in the browser console. Any idea why this works on the second time and not the first time?
Remember, the Child Component is mounted/loaded only when I click the Open Dialog on the Parent ponent.
References:
Call child method from parent
I am trying to call a function in my child ponent from a button click event in my parent ponent.
Parent Component:
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>
);
}
}
In the above code, the Parent ponent renders a Child ponent modal dialog (based on Material-UI) on click of a button. The Save button, part of the Dialog
ponent in Parent, when clicked should call a save function in the Child
ponent. As you can see, I have passed a callback function handleSaveDialog
through the Child
ponent props named handleSaveData
. The save button click will call handleSaveClick
on the child, once the Child ponent mounts and passes the callback to the Parent ponent.
Child Component:
class Child extends Component{
constructor(props){
super(props);
this.state = {
//..
}
}
ponentDidMount(){
console.log('mount');
this.props.handleSaveData( () => this.handleSaveClick());
}
handleSaveClick = () => {
console.log('save clicked');
}
render(){
return(
<div>
//..
</div>
);
}
}
In the above code, I am using the accessing the callback function passed by the Parent
ponent props and binding it to the Child
ponent's save fucntion handleSaveClick
.
Problem:
When I click the Open Dialog button in Parent, for the first time, the Dialog
mounts the Child ponent. However, the click on Save
button does not work (no error). After, closing the dialog, when I reopen the Dialog and click on Save, the handleSaveClick
in the Child dialog is triggered and a message is logged in the browser console. Any idea why this works on the second time and not the first time?
Remember, the Child Component is mounted/loaded only when I click the Open Dialog on the Parent ponent.
References:
https://material-ui./ponents/dialogs/#form-dialogs
Call child method from parent
https://github./kriasoft/react-starter-kit/issues/909#issuement-390556015
Share Improve this question edited Jul 18, 2019 at 6:56 Souvik Ghosh asked Jul 18, 2019 at 6:49 Souvik GhoshSouvik Ghosh 4,61713 gold badges59 silver badges83 bronze badges 1- Can you put your code somewhere like codesandbox or in stackblitz. Its hard to tell from here. – Shubham Verma Commented Jul 18, 2019 at 6:59
2 Answers
Reset to default 5It will not work because if you console log this.handleSaveClick
in render
function it will be undefined
as there is no re-render. So there are 2 ways to go for this:
- Saving function in the state. Link: https://codesandbox.io/s/fervent-browser-gw75c
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 {
ponentDidMount() {
console.log("mount");
this.props.handleSaveData(this.handleSaveClick);
}
handleSaveClick = () => {
console.log("save clicked");
};
render() {
return <div>//..</div>;
}
}
- Using
ref
. Link: https://codesandbox.io/s/musing-kalam-nj29n
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>;
}
}
- Using callback to save instance and than use arrow function:
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 {
ponentDidMount() {
console.log("mount");
this.props.handleSaveData(this.handleSaveClick);
}
handleSaveClick = () => {
console.log("save clicked");
};
render() {
return <div>//..</div>;
}
}
You will need to use arrow function in onClick
as it will create a new function every time we click and thus getting a new instance of handleClick
. And if you pass this.handleClick
it won't work because it is undefined
. You can check this by logging the value of this.handleClick
in render
function.
Note: Use the 2
option its more reliable.
Hope this helps!
Well, I don't know why you have this sort of scenario but the first things that es in my mind is that you can write handleSaveClick
method in your Parent ponent. And in case you need to use that function on any action that might be happening in your child ponent, you can pass this function as a prop from parent ponent. So your both cases can be handled
- You can call this method on something happening in parent ponent
- You can use the same method on any action that is happening in your child ponent.
If still you think that you have to define the method in your child ponent, you can use the refs