#ActuallyAutistic web dev. Does front of the front-end. Loves perf and minimalism. Prefers HTML, CSS, Web Standards over JS, UX over DX. Hates div disease.
The code within useEffect(() => {}, []) is executed only once. The value for itWorks upon creation of the function is true and consequently it will always be true.
There are two ways you can fix this.
Method 1: update listener each time it changes.
import{useState,useEffect}from'react'constHandleKeypress=()=>{const[itWorks,setItWorks]=useState(true)useEffect(()=>{// reference must be same for addEventListener and removeEventListener// = can't use inline arrow function!functionlistener(event){if(event.code==='Space')setItWorks(!itWorks)}document.addEventListener('keypress',listener)return()=>{document.removeEventListener('keypress',listener)}},[itWorks])return(<div><p>{itWorks?'It works!':'It does not'}</p><buttononClick={()=>setItWorks(!itWorks)}>Press me</button></div>)}exportdefaultHandleKeypress
This works because now each time that itWorks changes the listener is updated.
Method 2: use setState with a function
import{useState,useEffect}from'react'constHandleKeypress=()=>{const[itWorks,setItWorks]=useState(true)useEffect(()=>{document.addEventListener('keypress',(e)=>{if(e.code==='Space')setItWorks(state=>!state)})},[])return(<div><p>{itWorks?'It works!':'It does not'}</p><buttononClick={()=>setItWorks(!itWorks)}>Press me</button></div>)}exportdefaultHandleKeypress
The function gets the current value in the state so if you're only toggling it you can simply reverse it. This works for all cases where you determine the next state based on the previous state.
#ActuallyAutistic web dev. Does front of the front-end. Loves perf and minimalism. Prefers HTML, CSS, Web Standards over JS, UX over DX. Hates div disease.
The second one causes slightly less work for CPU to do, although the effect is quite minimal with today's computing power. And the work only happens when the value changes, in this case from user action, so not really a big argument for it.
However it is always a good idea to clean up what you create to the DOM, so in this case by adding the removeEventListener part even if using the second approach.
The code within
useEffect(() => {}, [])
is executed only once. The value foritWorks
upon creation of the function istrue
and consequently it will always betrue
.There are two ways you can fix this.
Method 1: update listener each time it changes.
This works because now each time that
itWorks
changes the listener is updated.Method 2: use
setState
with a functionThe function gets the current value in the state so if you're only toggling it you can simply reverse it. This works for all cases where you determine the next state based on the previous state.
You still want to remove the event listener on unmount in the second example though!
Thanks for the insights! With these corrections both methods work indeed!
Is there a reason to prefer one over the other?
The second one causes slightly less work for CPU to do, although the effect is quite minimal with today's computing power. And the work only happens when the value changes, in this case from user action, so not really a big argument for it.
However it is always a good idea to clean up what you create to the DOM, so in this case by adding the removeEventListener part even if using the second approach.
Thank you! Makes sense.