Reputation: 4250
I created a HOC to listen for clicks outside its wrapped component, so that the wrapped component can listen and react as needed.
The HOC looks like this :
const addOutsideClickListener = (WrappedComponent) => {
class wrapperComponent extends React.Component {
componentDidMount() {
console.log('*** component did MOUNT called ***');
document.addEventListener('click', this._handleClickOutside.bind(this), true);
}
componentWillUnmount() {
console.log('*** component will UNMOUNT called ***');
document.removeEventListener('click', this._handleClickOutside.bind(this), true);
}
_handleClickOutside(e) {
const domNode = ReactDOM.findDOMNode(this);
if ((!domNode || !domNode.contains(e.target))) {
this.wrapped.handleClickOutside();
}
}
render() {
return (
<WrappedComponent
ref={(wrapped) => { this.wrapped = wrapped }}
{...this.props}
/>
);
}
}
return wrapperComponent;
}
It works fine... most of the time.
When the wrapped component is unmounted, the method "componentWillUnmount" gets called, but "removeEventListener" continues to listen for events, and so I get error messages "Uncaught Error: findDOMNode was called on an unmounted component."
Any ideas what could be causing this ?
Upvotes: 3
Views: 1257
Reputation: 2214
Add this to constructor:
this._handleClickOutside = this._handleClickOutside.bind(this)
and then do this:
componentDidMount() {
document.addEventListener('click', this._handleClickOutside, true);
}
componentWillUnmount() {
document.removeEventListener('click', this._handleClickOutside, true);
}
Upvotes: 1
Reputation: 37594
The reason why your removeEventListener
is not working is because you are trying to remove a newly created function. The same applies for addEventListener
. These are two completely difference functions and have no reference to each other whatsoever.
You have to bind your method _handleClickOutside
so React knows there exactly one version of it. You do that by binding it in the constructor with
constructor() {
super();
this._handleClickOutside.bind(this);
}
or auto bind it with ES6 arrow methodology
_handleClickOutside = (e) => { ... }
Now you pass the binded method to your eventlisteners with
document.addEventListener('click', this._handleClickOutside, true);
and respectively the remove listener.
Upvotes: 3