Reactjs, removing event listener on componentWillUnmount, - javascript

I have a small react app. in One of my main components, I am adding an event listener on componentDidMount but when I try to remove it on componentWillUnmount it does not seem to be doing it. I have even tried putting them one after the other and still it does not seem to be removed.
Here is sample code of my situation (reduced from the real one)
listenerF(e){
console.log('im scrolling!'
//..actually does other stuff
}
componentDidMount(){
window.addEventListener('scroll', e => this.listenerF(e))
}
componentWillUnmount(){
window.removeEventListener('scroll',e=>this.listenerF(e))
console.log('unmounted')
}
I suspect it might be the arrow function acting as an anonymous function, and therefore removeEventListener isn't matching the functions properly.
I tried some alternatives to add the event listener with just a function call or without an arrow function, but it didnt seem to work like that, so Im not sure what may be wrong with my syntax or setup that the event is getting added right, but cannot remove it

React injects the event to your handler automatically there is no need to wrap it in an arrow function just to pass the event, the problem is that React had no refference to the function that you passed to your handler.
listenerF(e) { // dont forget to add the event as a parameter here
console.log(e);
console.log("im scrolling!");
}
componentDidMount() {
window.addEventListener("scroll", this.listenerF);
}
componentWillUnmount() {
window.removeEventListener("scroll", this.listenerF);
console.log("unmounted");
}

The problem here is that you are using two different functions: One that is created when you add the event listener (the arrow function). And a different one that you use when you try to remove the listener (the arrow function.)
As the documentation state:
The event listener to be removed is identified using a combination of the event type, the event listener function itself, and various optional options that may affect the matching process.
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener

Related

variable, passed together with event, does not change

When I call editWorkout function for the first time, the variable 'workout' gets passed in the editWorkout2 function, but then, during the second time, when the same thing happens, but in this case 'workout' has new value, this value for some reason won't change in editWorkout2 function. How can I fix that?
function editWorkout(e, workout) {
e.stopImmediatePropagation();
saveBtn.addEventListener("click", function (e) {
console.log(workout);
editWorkout2(e, workout);
}.bind(this)
);
}
on each call of your editWorkout function, you're adding a new event listener to your "saveBtn" element with the same event type.
I recommend you read the usage notes of addEventListener.
So, the problem was as follows: because during the second call and all of the consecutive calls of the function editWorkout the event listener was going on top of the previous one, for some reason variable workout was not changing in that eventListener as a parameter. So I removed event listener in the editWorkout2 function, and everything started working fine

how could the callback function of addEventListener() works even if we didn't pass the event object into the function

const eventHandler = function (eventObject) {
console.log('You clicked on the page')
console.log(eventObject)
}
document.addEventListener('click', eventHandler)
So the codes above, as you can see we are listening to the click event on the page, with a "seperated" callback function, or an event handler function named eventHandler. And you can see this eventHandler function it expects an eventObject parameter which is the event object itself.
You can see, when i pass the function eventHandler into the addEventListener() method, i didn't passing any argument, right? So i didn't pass the eventObject into it. But, when i run the codes, the codes still running without error, so when i clicked on the page, i get a You clicked on the page message, and the event object.
Why is that? How could that function can log the event object into the console, when it requires us to pass the event object into it as an argument and we didn't do that? And that is my question. I got into this situation when playing around with DOM event, and it really make me confused. So hopefully someone can help me understand, what's going on here! Thank you very much!
Sorry you may want to run this codes on yourself, I will update the demo later, thank you so much!
The reason you don't need to pass an event as a parameter to the function eventHandler that you are calling in the document onclick event listener, is because it already has an event that has been registered to it in the function
const eventHandler = function (eventObject) {
console.log('You clicked on the page')
console.log(eventObject)
}
// document.addEventListener('click', eventHandler)
// This function above is the exact same as the one here below
// That's why it works, because the event object has already been passed to the function
document.addEventListener('click', function(eventObject){
console.log('You clicked on the page')
console.log(eventObject)
})
<h2>Click anywhere to run the function</h2>

How to pass an argument to the external function in addEventListener

OK this question already has a lot of answers in web, I have read all of them and I don't think any of them fit my situation. After tried like 2 hours I decide to give up and ask the people here:
This is the simplified example: I have a function, which will be triggered by a button click, I will call it click and it has an parameter with it. Inside the click function, I need to add an event listener to the window object use addEventListener().
I need the event to be passed to the handleKeydown(), because I need to know if the shift key (keycode == 16) is pressed down and I need to pass the parameter map to the handleKeydown() because I need to do something on it. after that, there will be more things done, let's say it has a function to draw pictures on the map
So I write the codes below:
function click (map) { // can be called multiple times, so multiple listener are attached, not needed
this.addEventListener("keydown", function(e) {handleKeydown(e, map)}, false)
drawPictureOnMap(map);
}
function deletePictureOnMap() {
// user always call this function to delete the picture before they
// call the click() to draw a new one
// delete the picture on map
}
function handleKeydown(e, map) {
if (e.keyCode == 16) {
// do something to map, e. g.:
// map.getLayers()
}
}
It works all fine until today, I find a big problem with it.
Because the function click can be called multiple times, so I found that multiple event listener are attached to the window object. That has a negative effect on the performance.
The idea is to attach a removeEventlistener() to another function, which is mean to delete the picture on map. Because, the user always delete the picture before they draw a new one.
The Problem is, I can't remove a event listener with anonymous function. But if I want to use external function, I can't find a way to pass the map parameter to it.
Another Idea is, make the map parameter global, so I don't need to pass it to the handleKeydown. But I am not willing to do it.
There are a few ways to solve this. Without more information, the one that comes to mind is this: Maintain a WeakMap of the handlers for a given element (a WeakMap so that if the element is removed and all references to it are dropped, the map entry automatically gets removed). Even IE11 supports WeakMap (enough of it, anyway).
const elementClickHandlers = new WeakMap();
Within that element-to-handlers map, store a Map of the handlers keyed by map.
Then in click, use those maps:
function click (map) { // can be called multiple times, so multiple listener are attached, not needed
let handlers = elementClickHandlers.get(this);
if (!handlers) {
handlers = new Map();
elementClickHandlers.set(this, handlers);
}
let handler = handlers.get(map);
if (!handler) {
handler = function(e) {handleKeydown(e, map)};
handlers.set(map, handler);
}
this.addEventListener("keydown", handler, false)
drawPictureOnMap(map);
}
When removing, find the handler for the element and map and remove that:
const handlers = elementClickHandlers.get(theElement);
if (handlers) {
const handler = handlers.get(map);
if (handler) {
handlers.delete(map);
someElement.removeEventListener("click", handler, false);
}
}
But there may be simpler ways to solve the problem we could suggest, if you showed a proper MCVE.

removeEventListener without knowing the function

Some of the third party plugin will attach the eventListener into the site. How to I remove the eventListener without knowing the function that attached.
I refer this removeEventListener but I can't get any clue to remove this.
Eg: getEventListeners(window) shows the events attached. But, when I try to remove the event using window.removeEventListener("eventname") is not working without knowing that function.
Please help, Thanks in advance.
getEventListeners(window) will return a map of events and their registered event listeners.
So for DOMContentLoaded event for example you can have many event listeners. If you know the index of the listener you want to remove (or if there exists only one), you can do:
var eventlistener = getEventListeners(window)["DOMContentLoaded"][index];
window.removeEventListener("DOMContentLoaded",
eventlistener.listener,
eventlistener.useCapture);
Unfortunately, you cannot do that. You need to have a reference to the event handler function in order to remove it by removeEventListener.
Your only option if you cannot get that reference would be by entirely replacing that Node.
Update: 2023
EventListeners can be removed without knowing the actual function reference. But it will only work in modern browsers.
Use AbortController to remove the event. With AbortSignal, you can simply get the signal to remove it for you:
Sample Code:
const controller = new AbortController();
const { signal } = controller;
window.addEventListener('resize', () => doSomething(), { signal });
controller.abort(); // It wll remove the "resize" event handler.
You can check and add a polyfill for older browsers

Removing DOM Event Observer

I'm working in a javascript based system with some legacy code (ominous music), and this legacy code adds event listeners like this
foo.addEventListener("click",function(){
//do some stuff
});
Is there a way for me to programmatically remove event listeners that have been added like this? I know about removeEventListener, but's it's not clear from the documentation how (if at all) to remove a listener which a programmer added via an anonymous function.
As far as I can tell, you can't use an anonymous function if you want to call removeEventListener because you need a reference to the same function that you used with addEventListener and the only way to do that is to have a name for the function (e.g. not anonymous).
Similar question and conclusion here: removeEventListener on anonymous functions in JavaScript
Without changing the structure of your code, you can give it a name and then use that name later.
foo.addEventListener("click", function fooClickHandler(){
//do some stuff
});
// then later
foo.removeEventListener("click", fooClickHandler);
You can remove them the same way that you add them by passing in the handler that you created it with. The handler ceases to be an anonymous function at this point though:
var handler = function() {
// do some stuff
};
foo.addEventListener("click", handler);
foo.removeEventListener("click", handler);
You can do some funky stuff like this to remove handlers with anonymous functions although I don't recommend it, but it is possible and you can't access callee in strict mode:
document.addEventListener('click', function(e) {
console.log('click');
if(e.unbind) {
document.removeEventListener('click', arguments.callee);
}
});
var event;
// First actual real event
event = new Event('click');
document.dispatchEvent(event);
// Unbinding event
event = new Event('click');
event.unbind = true;
document.dispatchEvent(event);
// Should not fire
event = new Event('click');
document.dispatchEvent(event);
If you want to get rid of all eventlisteners before adding your own you can use clonenode and remove original. The copy will not have the eventlisteners copied with it.
var fee = foo.cloneNode();
foo.parentNode.replaceChild(fee, foo);
Should look the same but without eventlistener.
Got a fiddle here that proves my point: http://jsfiddle.net/U7w7M/1/
Notice how the formatting stays, the position is the same, but click action is removed after first click

Categories

Resources