eventListener for hashChange - reactjs

I am trying to add an event listener for my page in ReactJS. I have added it to componentDidMount(). But the is only triggered for the initial page load.
How can I get it trigger whenever there is a change in the address bar.
componentDidMount:function(){
window.addEventListener("hashchange", console.log('hashchange1'));
$(window).bind("hashchange", console.log('$ - hashchange'));
window.onhashchange = console.log('hashchange3');
ReactDOM.findDOMNode(this).addEventListener('hashChange',console.log('ReactDOM - hashchange'));
},
I've tried several different ways to get it to work, but they all work only on the first load. What am I doing wrong?
Thanks.

You're just executing the console.log in every case, and adding its return value as the listener. You need to pass a function as an event listener, for example:
window.addEventListener("hashchange", e => console.log('hashchange1', window.location.hash ));
https://jsfiddle.net/ku6okrp2/
EDIT to make it look more obvious using ES5:
window.addEventListener("hashchange", function(e){
console.log('hashchange1', window.location.hash )
});

Related

Adding Click Event Listener to Adobe Data Layer in React

I'm just starting to enter the world of Adobe Analytics. I'm working in React with Typescript and I am trying to leverage the adobe data layer to send information to Adobe Launch. I've been able to successfully use the adobe push function (i.e. window.adobeDataLayer.push({ test: 'test succeeded' })) and can both see that in the console via window.adobeDataLayer.getState() and ran a simple test to confirm it made its way to Adobe Launch.
However, when it comes to adding an event listener, I'm stumped. I attempted to follow Adobe's Documentation and came up with the following (doStuff() was just to confirm that eventListeners were working as expected, which they do):
function myHandler(event: any): void {
console.log("My handler was called")
}
function doStuff() {
console.log('do stuff was called')
}
function adobeAnalyticsTest(): void {
console.log(' adobeAnalyticsTest function called')
window.adobeDataLayer = window.adobeDataLayer || []
window.adobeDataLayer.push({ test: 'test succeeded' })
window.addEventListener('click', doStuff)
window.adobeDataLayer.push(function (dl: any) {
dl.getState()
dl.addEventListener('click', myHandler)
})
}
useEffect(() => {
adobeAnalyticsTest()
}, [])
Looking at window.adobeDataLayer, I couldn't see anything that seemed to indicate there was a click event listener (although this could be ignorance on my part) nor was 'My Handler was called' ever logged to the console. Does anyone have any ideas as to what I'm doing wrong or know how to tell when it's working correctly?
At face value, it looks like you don't have anything in your code that actually calls adobeAnalyticsTest().
Also, the listener you attach to dl doesn't listen for DOM events like clicking on something in window (like your window.addEventListener line); it listens for payloads pushed to adobeDataLayer where the passed event property value matches what you are listening for.
For example, put adobeDataLayer.push({'event':'click'}) in your js console you should see "My handler was called".
Think of it more like subscribing to a CustomEvent (because that's what it is, under the hood), rather than a native DOM event.

Why do I need cleanup in useEffect in react

Im new to react and Im not sure why do we need a cleanup function when dealing with EventListeners, Iam trying to set an event when resizing my window but i only have 1 event in (Elements -> Event Listeners) tab in chrome dev tools, even if I don't return a cleanup function in my hook
Heres my code:
useEffect(() => {
window.addEventListener("resize", function checksize() {
console.log("1");
});
});
First of all, you should absolutely avoid using that in your code, because on each rerender, it is going to add a new event listener to the window.
Secondly, to answer your question, you should have a cleanup effect to remove event listeners and other similar mechanisms to avoid memory leaks. If you don't clean them up, then you leave dangling eventlisteners taking up memory which is not a good idea, and may be picked up on within your other components as well. So the ideal way to handle this kind of code is
const logOne = () => console.log("1"); //put the function reference in a variable so that you can remove the event afterwards
useEffect(() => {
window.addEventListener("resize", logOne);
return () => {
window.removeEventListener("resize", logOne); //remove event listener on unmount
}
}, []); //empty dependency array so that function only runs on first render, so that consequent rerenders don't cause you to add more of these event listeners
Because you will keep adding listeners every render. Instead of printing "1" once, it will print it twice next time. Then 3 times on the next re-render and so on.

Using this.setState is changing my event param values

I'm working with react typescript component, and what I've noticed is that when I'm using this.setState, then it is is changing my event param values. The case is that I'm using a combobox which is calling on an event called handleChange.
<ComboBox
change={this.handleChange}
value={"test"}
/>
Under is the handler:
handleChange = (e) => {
$.get('/webapi/getItems?ID=' + e.target.value.id, (data) => {
this.setState({ textEditorValue: data });
});
}
It seems like the onChange function is being ran two times.
Because first the event param in the function has the correct values, then suddenly it changes to the basic object for events, also it runs two times the setState / the function.
How can I fix this?
There is something strange in your code, you seems want to fetch something with you api, but you do nothing with the result aka data.

Bubble is not firing when programmatically Input changed

Having an Input from semantic-ui-react changed when the User writes something, does fire onChange and any parent component that includes the Input fires onChange as well. A process called bubble .... right?
Example
<li onChange={this.onListChange}>
<Input onChange={this.onInputChange}/>
</li>
Any change made in the Input, fires onInputChange() and onListChange() together one after another.
Although, when Input.value/text/content is changed dynamically, Input.onChange() is not called at all. So you have to call the onChange() function manually.
setFoo() {
this.input.value = 'foo';
this.inputChange();
}
But the problem here is, that when we call inputChange we don't get as e.target the component Input, nor data has anything to do with it, and the most important ... :
No Bubbling effect.
Which means that onListChange() will not be fired.
Question:
How can I achieve a proper bugless bubbled onChange() on Semantic-UI-React?
Not exactly sure about semantic-ui-react because I've never used it, but this in theory should work (it works for a normal input):
setFoo() {
this.input.value = 'foo';
const event = new Event('input', { bubbles: true });
this.input.dispatchEvent(event);
}

How to avoid destroying backbone view unnecessarily?

I have a view in which two tabs are shown.
Tab visibility is controlled simply by css class. Here is the code:
class PlansView extends Backbone.View
className: 'plans-view tab1-selected'
events:
'click .btn-buy': 'buyItems'
'click .tab1': 'switchToTab1'
'click .tab2': 'switchToTab2'
switchToTab1: (event) =>
this.$el.toggleClass 'tab1-selected', no
this.$el.toggleClass 'tab2-selected', yes
window.location.hash = 'tab1'
switchToTab2: (event) =>
this.$el.toggleClass 'tab1-selected', yes
this.$el.toggleClass 'tab2-selected', no
window.location.hash = 'tab2'
I use the window.location.hash in the functions because when the tab switches, I want the url to reflect this. i.e. When the url is mycompany.com/view#tab1, tab 1 is activated. If it is mycompany.com/view#tab2, i want to show the tab 2.
However what happened is that: when the hash is changed, the router is triggered! The view is then unloaded and then loaded again. It shows a very clear visual jerking.
It is the relevant code in the router:
_showSection: (event) ->
sectionView = new PlansView event
#previousView?.remove()
#previousView = sectionView
#$sectionHolder.append sectionView.el
If I remove the window.location.hash statements, the tab switches very smoothly but the url will stay unchange.
For some reason the pushState is disabled in the project. I don't think I can change this for now.
new Router()
Backbone.history.start pushState: false
Is there anyway I can update the hash without triggering the router code.
Use Backbone.history.navigate instead of window.location.hash.
Backbone.history.navigate '#tab1'
The reason the router is triggered is because Backbone listens to hash changes and triggers route accordingly.
The default behavior of the navigate function is only to change the hash, and if you want to trigger the route, you need to explicitly set the trigger: true option.

Resources