My problem with the code below is: Why is HP displayed as "1" when the alert is thrown and only set to "0" after I confirm the alert? Shouldn’t the component be rerendered before the alert is thrown and therefore already been displayed as 0?
At least this is what I conclude from my understanding of useEffect which reads like this:
useEffect is a hook provided by React that allows you to run side effects in your functional components. It is run after every render cycle of the component.
By default, useEffect runs after the initial render and every update.
However, you can control when it runs by passing an array of
dependencies as the second argument. This array contains values that
the effect depends on, and the effect will only be run when any of
these values change.
screenshot for better understanding:
code sandbox link for the code https://codesandbox.io/s/mystifying-scooby-4t2x0s
import "./styles.css";
import { useState, useEffect } from "react";
export default function App() {
const [hp, setHp] = useState(3);
useEffect(() => {
if (hp === 0) {
alert("You won!");
}
}, [hp]);
function handleClick() {
setHp((prev) => {
return prev - 1;
});
}
return (
<div className="App">
{<h1>HP: {hp}</h1>}
<button style={{ fontSize: "24px" }} onClick={handleClick}>
Reduce HP by 1
</button>
</div>
);
}
I did some google/youtube research but couldn’t find an explanation.
2
Answers
Your understanding of
useEffect
is in fact correct!However
alert
is a bit special as it basically blocks any render in the browser until it has been closed.You can simply wrap your alert in a setTimeout, even with no delay, as it allows the render cycle to re-render before alert blocks the render;
But even better would probably be not to use an
alert
, and instead make some nice "in-game" graphics? 😉To combine comments into an answer,
alert
waits until the user dismisses the dialog; with that in mind you may want to implement a modal yourself or use react-modal library or find ideas on YouTube.If you still want to use
alert
method, the workaround will be to usesetTimeout
conditionally inside theuseEffect
like so:CodeSandbox: https://codesandbox.io/s/currying-browser-4ou7lb
However, this is redundant to call
useEffect
every timehp
updates is unnecesary.To boost your app performance, taking in consideration that
handleClick
is pure function that only reduceshp
by-1
you can use it to your advantage to have the alert in there.(And as a side-note in your real app I’d personally suggest you rename it to
reduceHPbyOne
or something more descriptive.)Code will be like this:
CodeSandbox link: https://codesandbox.io/s/gallant-roentgen-x4cdj8
The code is pretty simple with a little modification: when
hp===1
thenalert('You won!')
because, again,handleClick
decreaseshp
by-1
.You are certain
setHp
will re-render withhp-1
update, and in a case scenario wherehp===1
condition istrue
, what happens is: React updateshp
to0
andalert
is run afterwards.