I am experiencing a very weird bug in a basically fresh react project. I am attempting to update an array whenever the browser receives a keypress event. For some reason the update that I would expect to work is only editing the last value of the array or keeping it at a fixed length of 1.
function Component() {
const [keypresses, setKeypresses] = useState([]);
function updateKeypresses(e) {
setKeypresses([...keypresses, e.keyCode]);
}
useEffect(() => {
window.addEventListeneder('keydown', updateKeypresses);
return () => {
document.removeEventListener('keydown', updateKeypresses);
}
}, []);
useEffect(() => {
console.log(keypresses);
}, [keypresses])
}
Output: I dont have codes memorized so image nums
press a
~> [a]
press b
~> [b]
press c
~> [c]
What I would expect:
~> [a]
~> [a,b]
~> [a,b,c]
3
Answers
The reason that the
keypresses
array always reset it because your pointer of theupdateKeypresses
function isn’t changed.to fix it you have to add the
keypresses
state to the dependecies array of useEffect:I think that you should update your state like this:
Because, when updating state based on the previous state, it’s recommended to use the functional form to ensure that you are working with the most up-to-date state.
Function
updateKeypresses
is created on each render and always have latest version ofkeypresses
. However, your effect in run only after first render, and it ‘snapshots’ version ofupdateKeypresses
available on first render. At this momentkeypresses
is empty, and every time event is triggered, your code effectively executes:To fix this you can either memoize function and put it into effect dependencies:
Or, a bit simpler way it to use function with
setKeypresses
:This way, React will run callback function providing latest value of
keypresses
as parameter (prevKeypresses
) and so you can update state without re-attaching event listener every timekeypresses
changes