Search code examples
javascriptreactjsaddeventlistener

I have a piece of code that does not work as expected, need an explanation


import React,{useEffect,useState} from 'react';

export function App(props) {
  let [state,setState]=useState(2);
  console.log('outside',state);

  useEffect(()=>{
    document.querySelector('.btn').addEventListener('click',()=>{
    console.log('afterclick',state);
    setState(state-1);
    })

  },[state])

  return (
    <>
      <button className="btn">click me</button>
    </>
    
  );
}





the output looks like this(no click of button):

outside 2

the output looks like this(after one click of button):

outside 2
afterclick 2
outside 1

everything works as expected till now, the second click of button is where it gets confusing for me

clearly the value of state now is 1. as 'outside 1' is printed in the last line

output (after 2 clicks)

outside 2
afterclick 2
outside 1
afterclick 2  // why is 2 here instead of 1?
outside 1
afterclick 1
outside 0

why is 'afterclick 2' printed? instead of 'afterclick 1', as the value of state now is 1. why does it still print 2? this is where my confusion lies. i also do not understand why the two lines above 'outside 0', are printed. any explanation??

do tell me if any clarification is needed.

thank you!


Solution

  • You need to clean up the event listener when value of state is change or component is unmounted, this will correctly handles the state current value.

    import React,{useEffect,useState} from 'react';
    
    export default function App(props) {
      let [state,setState]=useState(2);
      console.log('outside',state);
    
      useEffect(()=>{
        const handleClick = () => {
          console.log('afterclick', state);
          setState(prevState => prevState - 1);
        };
    
        document.querySelector('.btn').addEventListener('click', handleClick);
    
        return () => {
          document.querySelector('.btn').removeEventListener('click', handleClick);
        };
    
      },[state])
    
      return (
        <>
          <button className="btn">click me</button>
        </>
        
      );
    }