Remove Event Listener On Unmount React
JavascriptReactjsJavascript 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;
};