DEV Community

Yar
Yar

Posted on

Why pressing the key works only once in my React project?

Hey there!

Could you tell me what's wrong with this code?

I have a button in my application that toggles the state.

I would like to achieve the same with pressing the spaัebar on the keyboard. And it works only one way. The state changes to false once. And then no reaction.

import { useState, useEffect } from 'react'

const HandleKeypress = () => {

    const [itWorks, setItWorks] = useState(true)

    useEffect(() => {
        document.addEventListener('keypress', (e) => {
            if (e.code === 'Space') setItWorks(!itWorks)
        })
    }, [])

    return (
        <div>
            <p>{itWorks ? 'It works!' : 'It does not'}</p>
            <button 
                onClick={() => setItWorks(!itWorks)}
            >Press me</button>
        </div>
    )
}

export default HandleKeypress
Enter fullscreen mode Exit fullscreen mode

What am I missing? ๐Ÿ˜ผ

Top comments (11)

Collapse
 
joelnet profile image
JavaScript Joel

This looks like an issue with stale values inside of useEffect.

Make this change:

// โŒ Stale value
if (e.code === 'Space') setItWorks(!itWorks)

// โœ… Correct value
if (e.code === 'Space') setItWorks(state => !state)
Enter fullscreen mode Exit fullscreen mode

I have created a video on this topic here:

Cheers ๐Ÿป

Collapse
 
ptifur profile image
Yar

Thanks Joel! :)

Collapse
 
caspergeek profile image
Eranda K.
useEffect(() => {
    document.addEventListener('keypress', (e) => {
        if (e.code === 'Space') setItWorks(!itWorks)
    })
}, [itWorks])
Enter fullscreen mode Exit fullscreen mode

This might work. But haven't tested though.

Collapse
 
ptifur profile image
Yar

Thanks Eranda! Seems like it doubles the number of added key listeners with each press and soon the application stops responding.

Which means the answer is to remove keyListeners and clean up useEffect an some point? Not sure how, I've reached the limit of my Hooks understanding ๐Ÿ˜œ

Collapse
 
caspergeek profile image
Eranda K. • Edited

When we add itWorks as a dependency to useEffect, It will re-render for each change of itWorks value. So here it will cause the number of event listeners to multiply at each change.
So as @Vesa Piittinen suggests it is a good idea to remove the listener if you are adding it in the useEffect hook.

Collapse
 
ptifur profile image
Yar

Thanks for the insights! With these corrections both methods work indeed!

Is there a reason to prefer one over the other?

 
ptifur profile image
Yar

Thank you! Makes sense.

Collapse
 
snorkypie profile image
Steeve Lennmark

You still want to remove the event listener on unmount in the second example though!

Collapse
 
ptifur profile image
Yar

Thanks for the explanation! I appreciate it.