Remove Event Listener On Unmount React

JavascriptReactjs

Javascript Problem Overview


I had higher order component in react like this:

export default function (InnerComponent) {
    class InfiniteScrolling extends React.Component {

        constructor(props){
            super(props);
        }

        componentDidMount() {
            window.addEventListener('scroll', this.onScroll.bind(this), false);
        }

        componentWillUnmount() {
            window.removeEventListener('scroll', this.onScroll.bind(this), false);
        }

        onScroll() {
            if ((window.innerHeight + window.scrollY) >= (document.body.offsetHeight - 50)) {
                const { scrollFunc } = this.props;
                scrollFunc();
            }
        }

        render() {
            return <InnerComponent {...this.props} />;
        }
    }

    InfiniteScrolling.propTypes = {
        scrollFunc: PropTypes.func.isRequired
    };

    return InfiniteScrolling;
}

After unmounting the component which are been wrapped via InfiniteScrolling, they where still throwing the error like (when I did scrolling):

> Warning: setState(...): Can only update a mounted or mounting > component. This usually means you called setState() on an unmounted > component. This is a no-op. Please check the code for the undefined > component.

Even though I did remove the scroll event on my component unmount. It didn't work.

But when I changed the code to like this:

constructor(props){
    super(props);
    this.onScroll = this.onScroll.bind(this);
}

componentDidMount() {
    window.addEventListener('scroll', this.onScroll, false);
}

componentWillUnmount() {
    window.removeEventListener('scroll', this.onScroll, false);
}

everything seems to be working fine, without any issues.

I feel they are exactly the same thing, but the second one works fine while the first one was throwing up the error in the console as mentioned before!

Javascript Solutions


Solution 1 - Javascript

.bind always creates a new function so you need to do like below, so it adds and removes the same function.

    constructor(props){
        super(props);
        this.onScroll = this.onScroll.bind(this); //bind function once
    }

    componentDidMount() {
        window.addEventListener('scroll', this.onScroll, false);
    }

    componentWillUnmount() {
        // you need to unbind the same listener that was binded.
        window.removeEventListener('scroll', this.onScroll, false);
    }

Solution 2 - Javascript

      componentDidMount() {
            window.addEventListener('scroll', this.onScroll, false);
        }

        componentWillUnmount() {
            window.removeEventListener('scroll', this.onScroll, false);
        }
        // use arrow function instead
        onScroll = () => { 
            if ((window.innerHeight + window.scrollY) >= (document.body.offsetHeight - 50)) {
                const { scrollFunc } = this.props;
                scrollFunc();
            }
        }

or you can use Arrow functions , to solve .bind(this) problems it worked form just fine.

Solution 3 - Javascript

I know it's a little bit late, but I just encounter this issue and wanted to share with you my solution, looking forward to any feedback. this solution includes react hooks. I hope you like

// Declare a static listener.
const eventListeners = useRef();

// now let's create our scroll Handler
const scrollHandler = useCallback(() => {...},[]);

useEffect(() => {
   // Here will be removing the static listener and then updated it for 
   // our new one since the first time will be empty it won't do anything.
    window.removeEventListener('scroll', eventListeners.current, true);

    // Then will set our current scroll handler to our static listener
    eventListeners.current = scrollHandler;

    // Here will be adding the static listener so we can keep the reference
    // and remove it later on
    window.addEventListener('scroll', eventListeners.current, true);
},[scrollHandler]);

Solution 4 - Javascript

A working version for my project with Arrow Function and no-bind:

componentDidMount = () => {
  window.addEventListener("wheel", this.onScroll, false);
};

componentWillUnmount() {
  window.removeEventListener("wheel", this.onScroll, false);
}

onScroll = (e) => {
  const item = this.refs.myElement;
  if (e.deltaY > 0) item.scrollLeft += 200;
  else item.scrollLeft -= 200;
};

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAnt&#39;sView Question on Stackoverflow
Solution 1 - JavascriptYury TarabankoView Answer on Stackoverflow
Solution 2 - JavascriptThe pyramidView Answer on Stackoverflow
Solution 3 - Javascriptdandush03View Answer on Stackoverflow
Solution 4 - JavascriptmsahinView Answer on Stackoverflow