Search code examples
javascriptevent-handlingreact-hooksonclicklistener

AddEventListener doesn't see scope


When I set a handler using OnClick it works fine and console.log always use the actual state. But when I use AddEventListener it works only once and then use the old state.

What the reason for than behaviour??

see the link https://codesandbox.io/s/blazing-star-wf3c9?file=/src/App.js

P.S Using [count] as useEffect dependence fix it, but it just create new listener each time, don't think that the right way to do this


Solution

  • You have a closure issue.

    In fact in each render your component going to create a new function showCount that you fixed the count.

    For exemple:

    In 1st render you create a function showCount that you fixed count = 1.

    In your 2nd render you create a function showCount that you fixed count = 2.

    In your 3 render you create a function showCount that you fixed count = 3.

    The issue is that you are not changing the showcount when your count changes like this.

    useEffect(() => {
        let buttonShow = document.querySelector(".buttonShow");
        buttonShow.addEventListener("click", showCount, true);
        return () => {
          buttonShow.removeEventListener("click", showCount, true);
        };
      }, [count]); 
    

    You can see the log count 2 because the reference of the count doesn't change when you use incremental.

    I think the best solution is to you use a Component class:

    import React from "react";
    
        class CounterAddEvent extends React.PureComponent {
          state = {
            count: 1
          };
          componentDidMount() {
            let buttonShow = document.querySelector(".buttonShow");
            buttonShow.addEventListener("click", this.showCount, true);
          }
          componentWillUnmount() {
            let buttonShow = document.querySelector(".buttonShow");
            buttonShow.removeEventListener("click", this.showCount, true);
          }
          increaseCount = () => {
            this.setState({ count: this.state.count + 1 });
          };
          showCount = () => {
            console.log(this.state.count);
          };
          render() {
            return (
              <div>
                <button onClick={this.increaseCount} className="input">
                  increase
                </button>
                <button>{this.state.count}</button>
                <button className="buttonShow">Show console</button>
              </div>
            );
          }
        }
        export default CounterAddEvent;