50
votes

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!

4

4 Answers

117
votes

.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);
    }
7
votes
      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.

2
votes

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]);

0
votes

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;
};