Running Firebase code when closing tab (React) - javascript

I am trying to run some firebase code when the tab is closed or refreshed for my react app, and it is working fine so far for firebase, however when I close the tab the code is not executed. I assume this is because firebase is asynchronous, and thus the tab closes before the firebase code is done executing. Is there a way for me to get around this and assure my firebase code finishes execution before the tab closes?
leaveLobby(e) {
e.preventDefault();
var firestore = firebase.firestore();
var docRef = firestore.doc("Games/Game " + this.state.Lobbycode);
docRef.get()
.then((docSnapshot) => {
if (docSnapshot.data().PlayerAmnt === 1) {
firestore.doc("Games/Active Games").update({
"Active Games" : firebase.firestore.FieldValue.arrayRemove(this.state.Lobbycode)
})
firestore.doc("Games/Game " + this.state.Lobbycode).delete();
} else {
docRef.update({
players : firebase.firestore.FieldValue.arrayRemove(this.state.name),
PlayerAmnt : firebase.firestore.FieldValue.increment(-1)
})
}
this.props.setInLobby(false, "", this.state.name);
})
return
}
componentDidMount() {
window.onbeforeunload = this.leaveLobby;
}

You can hook into the close event
window.addEventListener("beforeunload", (ev) =>
{
ev.preventDefault();
return ev.returnValue = 'Are you sure you want to close?';
});
componentDidMount: function() {
window.addEventListener('onbeforeunload', this.handleWindowClose);
},
componentWillUnmount: function() {
window.removeEventListener('onbeforeunload', this.handleWindowClose);
}
Just make sure you also store the handler in a local field during initialization in the constructor
this.handler = (ev) =>
Then use the following as needed
addEventListener("beforeunload", this.handler)
removeEventListener("beforeunload", this.handler)

Related

How to stop browser back button using react js hooks and history api?

I'm trying to disable back button after successful login into the application but couldn't make it.
I'm using react router dom 5.2.0 for routing.
Currently, I'm redirecting to login page if someone clicks to browser back button but I need to block the back button.
useEffect(() => {
return () => {
if (history.action === "POP") {
history.replace('/login');
}
};
}, [history]);
There is no way to fully disable the browsers back button, you can only prevent it from going back like you are doing.
When you are doing this, don't forget to listen for events on history, try this out:
useEffect(() => {
return history.listen(() => { // listen
if (history.action === "POP") {
history.replace("/login");
}
});
}, [history]);
This code works fine for me :
useEffect(() => {
return () => {
if (history.action === 'POP') {
history.go(1);
}
};
}, [history]);
Here is how you can stop the browser's back button
history.pushState(null, null, location.href);
window.onpopstate = function(event) {
history.go(1);
};

How to correctly add event listener to React useEffect hook?

I am trying to add an event listener to an Autodesk Forge viewer. This is an application built on React and this is the code I am trying:
const selectEvent = () => {
let viewer = window.NOP_VIEWER;
viewer.addEventListener(Autodesk.Viewing.SELECTION_CHANGED_EVENT, (e) => {
setSelection(e.dbIdArray);
});
};
This runs perfectly when called from a button onClick:
<Button onClick={() => selectEvent()}>Add</Button>
However, I would like the event listener to turn on when the page is loaded, so I tried useEffect:
useEffect(() => {
let viewer = window.NOP_VIEWER;
if (viewer) {
selectEvent();
}
}, []);
Even after trying some modifications, I could not get it to work. Nothing happens, so I suspect the event listener never gets added. Looking around at other solutions, event listeners are usually loaded with useEffect, but I am not sure what I am doing wrong. Any tips would be appreciated!
edit: It does enter the if statement, as a console.log works
Some background (might be relevant):
The viewer is loaded from a useEffect
useEffect(() => {
initializeViewer(props);
}, []);
and the viewer can be accessed as shown in the code above.
Try some thing like this.
When ever change in viewer and viewer is available, then you register the event.
Deregister the event handler as return function to hook
useEffect(() => {
if (viewer) {
viewer.addEventListener(Autodesk.Viewing.SELECTION_CHANGED_EVENT, (e) => {
setSelection(e.dbIdArray);
});
}
return () => { /* do the removeEventLister */ }
}, [viewer]);
Try this
NOP_VIEWER is a global variable to access the current Viewer
you need to remove the event listener after listening otherwise it will cause memory leak
useEffect(()=>{
NOP_VIEWER.addEventListener(Autodesk.Viewing.SELECTION_CHANGED_EVENT, (e) => {
setSelection(e.dbIdArray);
});
return()=>{NOP_VIEWER.removeEventListener(Autodesk.Viewing.SELECTION_CHANGED_EVENT, (e) => {
setSelection(e.dbIdArray);
}))}
},[])
or if it doesn't work
useEffect(()=>{
let viewer= window.NOP_VIEWER
viewer.addEventListener(Autodesk.Viewing.SELECTION_CHANGED_EVENT, (e) => {
setSelection(e.dbIdArray);
});
},[])

Saving local storage is being flaky - Cypress

I saw some other posts about local storage but they all relate to tokens and login.
We have an iframe that gets created and pops in from the right on our site upon first visit, I'm trying to keep this iframe from ever opening. A dev put an identifier in place for me to tell it's my Cypress test and to not fire the iframe but it's flaky.
I am using the plugin https://www.npmjs.com/package/cypress-localstorage-commands to handle my local storage.
This is in my Command file:
import "cypress-localstorage-commands";
In my test, I have the following:
beforeEach(() => {
cy.restoreLocalStorage();
cy.setLocalStorage('is_cypress_test', 'true');
})
afterEach(() => {
cy.saveLocalStorage();
})
However, this frequently fails and the iframe opens. When it works, it also prints out to console that Cypress was detected (this is something added on our sites code to verify it was working).
Here is the basic look of my test.
/// <reference types="Cypress" />
describe(`it browses to xxxx`, () => {
// sets up service cookie to preserve session
Cypress.Cookies.defaults({
preserve: 'foo',
});
beforeEach(() => {
cy.setLocalStorage('is_cypress_test', 'true');
cy.restoreLocalStorage();
})
afterEach(() => {
cy.saveLocalStorage();
})
it(`should log in via a POST, and browse xxx`, () => {
cy.serviceLoginByCSRF(Cypress.env('user_name'), Cypress.env('password'));
cy.visit('/#/asitepage');
});
describe(`it checks all xxxxx`, () => {
it(`should verify xxxxxx`, () => {
cy.get('h3').should('be.visible').invoke('text').then(data => {
let regex = /\n|\*|Back/g;
cy.textCleanup(data, regex).should('eq', 'bar');
});
});
});
describe(`it checks all yyyy`, () => {
it(`should verify yyyy`, () => {
cy.get('h3').should('be.visible').invoke('text').then(data => {
let regex = /\n|\*|Back/g;
cy.textCleanup(data, regex).should('eq', 'foo');
});
});
});
});
Beamer code
<!-- Beamer for product updates -->
<script>
var beamer_config = {
product_id: "foobar",
selector: "#beamer",
user_email: 'example#test.blogspot.gov',
user_firstname: 'Hank',
user_lastname: 'Williams',
filter: 'production',
onopen: function(){
// localStorage.setItem("is_cypress_test", "true") -- is test
if(localStorage.getItem("is_cypress_test")){
console.log("Skipping beamer load for Cypress");
return false;
}
}
};
</script>
<script type="text/javascript" src="https://asite.js" defer="defer"></script>
<!-- // Beamer for product updates -->
I'm wondering if I'm setting this in the wrong way, or wrong area?
Any help, or notes on how best to use this so it will always have that in localStorage before every test would be greatly appreciated.
Thoughts?
Thanks
Had the same issue, what fixed it for me was adding the following code to commands.js:
Cypress.LocalStorage.clear = function (keys, ls, rs) {
return;
}

React Native - run functions synchronously

I have written the following code, it runs smoothly but I have encountered a question:
submitFormToBackend = async () => {
if (this.paymentMethod === 'apple-pay') {
this.setState({ showLoadingIndicator: true }); // <-- below await setTimeout can confirm this line run before it
}
let requester = new ApplePayRequester({...this.form});
let applePay = new ApplePay();
await setTimeout(async () => {
let cardTokenResponse = await applePay.getCardToken();
if (cardTokenResponse.isSuccess()) {
requester.setCardToken(cardTokenResponse.message);
let response = await requester.pushToBackend();
this.setState({ showLoadingIndicator: false }); //<-- below setTimeout can confirm this line run before them
if (response.isSuccess()) {
setTimeout(() => { this.navigator.backToPreviousScreen(); }, 800);
} else {
setTimeout(() => { Alert.alert('your purchase has error. Try again'); }, 800);
}
} else {
this.setState({ showLoadingIndicator: false });
setTimeout(() => { Alert.alert('cannot get your card token.'); }, 800);
}
}, 800);
};
My render() in that component:
render() {
return (
<View style={styles.form}>
<LoadingIndicator visible={this.state.showLoadingShader} />
<InputBox />
<InputBox />
<SubmitButton />
</View>
);
}
As you see there are a lot of setTimeout() functions, it seems like functions will crash together if I don't use setTimeout() to restrict the functions run one by one.
However, it's not a good practice as there is no default millisecond for success running (the millisecond can set to 700ms or 1500ms or etc.). Therefore I would like to ask is there any solution to confirm previous function has run before next function start, other than using setTimeout()?
UPDATE
Procedures:
Step 1 - Press submit button
Step 2 - Pop up a confirmation modal
Step 3 - User confirm, dismiss confirmation modal, set showLoadingIndicator to true to show loading indicator
Step 4 - Call ApplePay and pop up ApplePay UI
Step 5 - User confirm, set showLoadingIndicator to false to dismiss loading indicator and navigate previous screen
Problems encountered when not using setTimeout():
Step 4 - ApplePay UI cannot pop up after setting showLoadingIndicator to true, below is the code that encountered problem:
let cardTokenResponse = await applePay.getCardToken();
Step 5 - Alert will be pop up before setting showLoadingIndicator to false, which stops the setting, below is the code that encountered problem:
this.setState({ showLoadingIndicator: false });
if (response.isSuccess()) {
} else {
setTimeout(() => { Alert.alert('your purchase has error. Try again'); }, 800);
}
A second optional parameter of setState function is a callback function that runs synchronously with the state change.
So you can just rely on the following:
this.setState({
//change state variables here
}, () => {
//do the next work here...
});
The callback function always run post the state is changed.
In your code, this would work:
this.setState({ showLoadingIndicator: false }, () => {
if (response.isSuccess()) {
this.navigator.backToPreviousScreen();
} else {
Alert.alert('your purchase has error. Try again');
}
});

vuejs2 adding event listener to component

I have a replies component and newReply component in my page. When a newReply is added, i emit an event on a global vue message bus to notify the replies component that a new reply has been added so that it reloads and re-renders.
methods: {
updateReplyList: function(newReply){
window.vueMessageBus.$emit('notifyingrepliescomponent',newReply);
},
}
i have attached the event listener for the notifyingrepliescomponent event inside the created() hook of replies component.
UPDATED CODE SNIPPET
//file: replies.vue
methods: {
fetch: function(url = null){
let vm = this;
if(!url)
{
axios.get(route('replies.paginated',{ 'thread' : this.thread_id }))
.then(function(serverResponse){
vm.replies_with_pagination = serverResponse.data;
//'replies_with_pagination' holds the paginated collection returned by laravel for the current paginated page
});
}
else
{
var page = url.match(/\?page=(\d+)/i)[1];
axios.get(route('replies.paginated',{ 'thread' : this.thread_id, 'page' : page }))
.then(function(serverResponse){
vm.replies_with_pagination = serverResponse.data;
});
}
},
reloadNew: function(url){
this.fetch(url);
window.scrollTo(0,0);
},
},
created() {
this.fetch();
window.vueMessageBus.$on('notifyingrepliescomponent',newReply => {
console.log('added object: ' + newReply);
this.all_reply_items.push(newReply);
this.reloadNew(route('replies.paginated',{ 'thread' : this.thread_id, 'page' : this.pageForQueryString }));
this.$emit('repliescountchanged',this.all_reply_items.length);
});
},
The whole system works, except for the first time. That is when there are no replies to a thread, and i add a new reply, the replies component does not reload. But for the subsequent replies, this works fine.
I am guessing this is an issue with my event listener? Can anyone please help?
TIA, Yeasir

Categories