There are few things causing the issue.
Setter does not update the count
value immediately. Instead it "schedules" the component to re-render with the new count
value returned from the useState hook. When the setText
setter is called, the count
is not updated yet, because the component didn't have chance to re-render in the mean time. It will happen some time after the handler is finished.
setCount(a => ++a); // <-- this updates the count after re-render
setText(count.toString()); // <-- count is not incremented here yet
You are calling addEventListener
only once and it remembers the first value of count
. It is good you have aNotWorkingHandler
in the dependencies - the onEffect
is being re-run when new count
and thus new handler function comes. But your added
flag prevents the addEventListener
from being called then. The button stores only the first version of the handler function. The one with count === 0
closured in it.
useEffect(() => {
if (!added && btnRef.current) { // <-- this prevents the event handler from being updated
btnRef.current.addEventListener("click", aNotWorkingHandler); // <-- this is called only once with the first instance of aNotWorkingHandler
setAdded(true);
} else {
console.log("New event handler arrived, but we ignored it.");
}
}, [added, aNotWorkingHandler]); // <-- this correctly causes the effect to re-run when the callback changes
Just removing the added
flag would, of course, cause all the handlers to pile up. Instead, just use onClick
which correctly adds and removes the event handler for you.
<button onClick={aNotWorkingHandler} />
In order to update a value based on another value, I'd probably use something like this (but it smells of infinite loop to me):
useEffect(
() => {
setText(count.toString());
},
[count]
);
Or compute the value first, then update the states:
const aNotWorkingHandler = useCallback(
(e) => {
const newCount = count + 1;
setCount(newCount);
setText(newCount.toString());
},
[count]
);
I agree with @nicholas-tower, that if the other value does not need to be explicitly set and is always computed from the first one, it should be just computed as the component re-renders. I think his answer is correct, hope this context answers it for other people getting here.