i have state vacations
, i set it after fetch
within useEffect
, i have button approve
that will change data in vacation
state and i want to re-render component after that happens within function handleApprove
, so i made up virtual state componentShouldUpdate
with initial value of false
and passed it as a dependency for useEffect
, and when function handleApprove
gets triggered, i setState
to the opposite of its value !componentShouldUpdate
, but the component only re-render when i click 2 times, why is that happening and why it works fine when i setState
componentShouldUpdate
from a child component ?
function VacationsComponent() {
const [vacations, setVacations] = useState([{}]);
const [componentShouldUpdate, setComponentShouldUpdate] = useState(false);
useEffect(() => {
const getVacations = async () => {
const response = await fetch("http://localhost:8000/get-vacations");
const data = await response.json();
setVacations(data);
};
getVacations();
}, [componentShouldUpdate]);
const handleApprove = async (e, vactionId) => {
(await e.target.value) === "approve"
? fetch(`http://localhost:8000/approve-vacation/${vactionId}`, {
method: "POST",
})
: fetch(`http://localhost:8000/reject-vacation/${vactionId}`, {
method: "POST",
});
setComponentShouldUpdate(!componentShouldUpdate);
};
<button onClick={(e) => handleApprove(e, item._id)}>
APPROVE
</button>
}
CodePudding user response:
This is most probably caused because useState hook operates asynchronously. Read more here.
You can update your code to use only one state like this
function VacationsComponent() {
const [vacations, setVacations] = useState([{}]);
const getVacations = async () => {
const response = await fetch("http://localhost:8000/get-vacations");
const data = await response.json();
setVacations(data);
};
useEffect(() => {
getVacations();
}, []);
const handleApprove = async (e, vactionId) => {
const slug =
e.target.value === "approve" ? "approve-vacation" : "reject-vaction";
await fetch(`http://localhost:8000/${slug}/${vactionId}`, {
method: "POST",
});
getVacations();
};
<button onClick={(e) => handleApprove(e, item._id)}>APPROVE</button>;
}
CodePudding user response:
put the setComponentShouldUpdate(!componentShouldUpdate) inside a thenable like this, and remove the async/await construct.
Also what was the intended purpose for setting state, I don't see the boolean being used anywhere. Usually when setting state you want the DOM to be updated somewhere, and especially with a boolean its great for toggling elements on the screen.
const handleApprove = (e, vactionId) => {
e.target.value === "approve"
? fetch(`http://localhost:8000/approve-vacation/${vactionId}`, {
method: "POST",
}).then(()=>{
// does this go here if it is approved or when it s rejected
setComponentShouldUpdate(!componentShouldUpdate);
})
: fetch(`http://localhost:8000/reject-vacation/${vactionId}`, {
method: "POST",
}).then(()=>{ setComponentShouldUpdate(!componentShouldUpdate); });
};