Question: React event listener with throttle not being removed

Question

React event listener with throttle not being removed

Answers 1
Added at 2017-09-07 20:09
Tags
Question

My code look something like this:

componentDidMount() {
    window.addEventListener('resize', this.resize);
}

componentWillUnmount() {
    window.removeEventListener('resize', this.resize);
}

resize = () => this.forceUpdate();

This works fine. But then I tried adding a throttle for better performance

componentDidMount() {
    window.addEventListener('resize', _.throttle(this.resize, 200));
}

componentWillUnmount() {
    window.removeEventListener('resize', this.resize);
}

resize = () => this.forceUpdate();

But then when I resize the screen in a different view I get this error warning:

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

That means I didn't correctly remove the listener. How do I remove a listener with a throttle? Or should I put the throttle somewhere else?

I tried updating componentWillUnmount likes this:

componentWillUnmount() {
    window.removeEventListener('resize', _.throttle(this.resize, 200));
}

but this did not work either.

Any ideas

Answers to

React event listener with throttle not being removed

nr: #1 dodano: 2017-09-07 20:09

Perhaps you could setup the throttle in the constructor:

constructor(props) {
   ...
   this.throttledResize = _.throttle(this.resize, 200)
}

componentDidMount() {
    window.addEventListener('resize', this.throttledResize);
}

componentWillUnmount() {
    window.removeEventListener('resize', this.throttledResize);
}

To elaborate on why this works, window.removeEventListener has to look at all registered event listeners for the target and event, and does an equality check on the event handler that was passed to it - if it finds a match, it removes it. Here is a naive implementation of that:

window.removeEventListener = function(event, handler) {
   // remove the supplied handler from the registered event handlers
   this.eventHandlers[event] = this.eventHandlers[event].filter((evtHandler) => {
       return evtHandler !== handler;
   })
}

_.throttle returns a new function every time you run it; therefore, the equality check will always fail and you will be unable to remove the event listener (without removing all event listeners). Here is a simple demonstration of what is happening in the equality check:

function foo() {}
function generateFunction() { 
   // just like throttle, this creates a new function each time it is called
   return function() {} 
}

console.log(foo === foo) // true
console.log(generateFunction() === generateFunction()) // false

Source Show
◀ Wstecz