'State isn't updated on handler call from child to parent
I have a parent component PlannerDetails and a child component PlannerDetailsFooter. Now when I click the button {props.emailConfig.buttonText} doesn't update with string Send Now, the boolean does though.
// PlannerDetails
import {useState} from "react";
import PlannerDetailsHead from "./PlannerDetailsHead";
import PlannerDetailsBody from "./PlannerDetailsBody";
import PlannerDetailsFooter from "./PlannerDetailsFooter";
const PlannerDetails = (props) => {
const [emailConfig, setEmailConfig] = useState(
{
'email': '',
'isSent': false,
'isHidden': true,
'buttonText': 'Send via Email'
}
)
const emailHandler = (event) => {
if (event.id === "mail-button") {
setEmailConfig({...emailConfig, isHidden: false})
setEmailConfig({...emailConfig, buttonText: 'Send Now'})
} else {
setEmailConfig(event.target.value)
}
}
return (
<div className="details-wrapper relative h-full">
<PlannerDetailsHead {...props}/>
<PlannerDetailsBody {...props}/>
<PlannerDetailsFooter {...props} emailConfig={emailConfig} emailHandler={emailHandler}/>
</div>
)
}
export default PlannerDetails
// PlannerDetailsFooter
import {TextField} from "@mui/material";
const PlannerDetailsFooter = (props) => {
return (
<div className="details-footer w-full p-4 bottom-0 absolute border-t-1">
{props.emailConfig.isHidden ? null :
<TextField
id="email-input"
label="Applicant's E-Mail"
variant="outlined"
required={true}
sx={{
width: '200px',
marginRight: '12px',
}}
value={props.emailConfig.email}
onChange={props.emailHandler}
/>}
<button id="mail-button"
className="send-mail p-2 bg-sx-pink rounded-md shadow-lg text-sx-purple-dark-soft"
onClick={props.emailHandler}
>{props.emailConfig.buttonText}</button>
</div>
)
}
Solution 1:[1]
First you'll want to debug to confirm whether this condition is true or not:
if (event.id === "mail-button")
Because if it's not then it looks like this is going to clobber your state object entirely:
setEmailConfig(event.target.value)
So it's not really clear at all what state update you want to make in that else block. It should probably look like your other state updates, at least structurally:
setEmailConfig({...emailConfig, someProp: 'some value'})
But aside from that, this is incorrect:
setEmailConfig({...emailConfig, isHidden: false})
setEmailConfig({...emailConfig, buttonText: 'Send Now'})
Only one of these will be applied, because they're both updating different things and state updates are batched and processed asynchronously. So whichever one of these occurs second will overwrite the changes made by the first one. (They will probably be executed in order, but I don't know if React guarantees that. And in any even, you shouldn't rely on that.)
Just perform one state update here:
setEmailConfig({...emailConfig, isHidden: false, buttonText: 'Send Now'})
You can perform multiple state updates, making use of the previous updates in the same batch by supplying a callback. For example:
setEmailConfig(prev => {...prev, isHidden: false});
setEmailConfig(prev => {...prev, buttonText: 'Send Now'});
But in this case that's kind of overkill. You'd use that in more complex logic where it's possible that you might have multiple separate things to update. But honestly in the majority of cases all you want to do is define the new state and call the setter with that new state, so one state update.
Solution 2:[2]
There is a mistake in your handler function.
1.
Especially, this is incore: setEmailConfig(event.target.value)
Because emailConfig is an object, but you are trying to set string value.
2.
Please use setState function with previous state.
Could you try as follows?
const emailHandler = (event) => {
if (event.id === "mail-button") {
setEmailConfig(prev=> ({
...prev,
isHidden: false,
buttonText: 'Send Now'
}))
} else {
setEmailConfig(prev=> ({
...prev,
email: event.target.value
}))
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Liki Crus |
