skip to Main Content
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!

2

Answers


  1. 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>
        </>
        
      );
    }
    
    Login or Signup to reply.
  2. On the second render (after clicking the button) the useEffect will be run again (since it depends on state, which has changed) and a new event listener will be attached. The old listener has captured the value of state and it will always log it as 2, while the second listener will always log it as 1 (the third as 0 and so on…).

    When attaching event listeners, you should clean them up, like so:

    useEffect(() => {
      const node = document.querySelector('.btn')
      const handleClick = () => {
        console.log('afterclick', state)
        setState(state - 1)
      }
    
      node.addEventListener('click', handleClick)
    
      return () => {
        node.removeEventListener('click', handleClick)
      }
    }, [state])
    

    However, a better way to handle button clicking, is to instead use the onClick prop, like so:

    const handleClick = () => {
      console.log('afterclick', state)
      setState(state - 1)
    }
    
    <button onClick={handleClick} />
    

    Unless you’re attaching listeners to window or document, using addEventListener in React should be a last resort.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search