I am working on a weird kind of site where i only need to send forms to firestore and save pdf files to firestorage. Not even runing any server-side code, just html and JS on the final client. My organization will only give an apache server path to run html, css and js. Later i'll need to read but i'm the only user with read access so it's not a big deal because i will run that locally on my node.js CLI.
But i am looking fordward to make my code as safe and efficent as possible even tho i have no idea what i'm doing 60% of the time.
Reading the docs and code online i realized that i am not doing unsubscribe() when i do my onAuthStateChanged(). By searching about memory leaks on JS i found a guy saying that if i meant to keep something live up in memory then i shouldn't unsubscribe from it. But as i said, i have no idea what i am doing.
Here is some of my .js and my login.html i am worried about 4 things, here i go:
- should i unsubscribe() after my sign-in-out onAuthStateChanged ends? if so, where?
- is it ok to set all this code inside window.onload ?
- Should i split this code into modules so i can re-use the initializeApp() and other repetitive code or it is not recommendabl since i am using CDN to import the firebase modules instead of using the import sentence.
- Am i suppossed to do the initializeApp() on every single one of my html pages? or doing it once on the login and forcing the user to login via vanilla JS will be the way to go?
window.onload = () => { //Config and init fb var firebaseConfig = { //...my config }; if (!firebase.apps.length) { firebase.initializeApp(firebaseConfig) } const auth = firebase.auth(); const provider = new firebase.auth.GoogleAuthProvider(); //Get elements const signInBtn = document.getElementById("log-in-btn"); //Listeners signInBtn.onclick = () => auth.signInWithPopup(provider); //Listeners require user //sign-in-out auth.onAuthStateChanged(user => { if (user) { // signed in if (!user.email.includes('@mydomain.com')) { auth.signOut(); //maybe delete the user, haven't test it alert('Porfavor utilice su correo institucional'); } else { window.location.replace('dashboard.html'); } } else { // not signed in you will always end up here } }); }
html are all something like this but i have a js file for each html, and each js configs and inits firebase.
<head> <meta charset="UTF-8"> <title>Login </title> <!--Project CSS--> <!--Boostrap 5--> <!--Firebase Modules--> <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js"></script> <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-firestore.js"></script> <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-auth.js"></script> <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-storage.js"></script> <!--Proyect Modules--> <script src="js/login.js" defer></script> </head>
and later in my dashboard this is my approach
window.onload = () => { var firebaseConfig = { //my config }; if (!firebase.apps.length) { firebase.initializeApp(firebaseConfig) } const auth = firebase.auth(); const provider = new firebase.auth.GoogleAuthProvider(); //Get elements const signedIn = document.getElementById("toggle-w-log-in"); const signedOff = document.getElementById("toggle-w-log-off"); const signInBtn = document.getElementById("log-in-btn"); const signOutBtn = document.getElementById("log-off-btn"); const uname = document.getElementById("userName"); const userDetails = document.getElementById("userDetails"); //Events / Listeners signOutBtn.onclick = () => { auth.signOut(); window.location.replace("login.html"); }; //Events / Listeners that require user //sign-in-out auth.onAuthStateChanged(user => { if (user) { // signed in signedIn.hidden = false; signedOff.hidden = true; userDetails.innerHTML = `<b>Sesión iniciada como ${user.displayName}</b>`; var contentImgUser = document.getElementById("img-Us"); contentImgUser.setAttribute("src", user.photoURL); } else { // not signed in signedIn.hidden = true; signedOff.hidden = false; userDetails.innerHTML = ''; window.location.replace("login.html"); } }); }
finally the million dollar question: am i going crazy with this code for my forms?
window.onload = () => { var firebaseConfig = { //my config }; if (!firebase.apps.length) { firebase.initializeApp(firebaseConfig) } const auth = firebase.auth(); const provider = new firebase.auth.GoogleAuthProvider(); //Get elements from dom by id //Events / Listeners //sign-in-out signOutBtn.onclick = () => { auth.signOut(); window.location.replace("login.html"); }; auth.onAuthStateChanged(user => { if (user) { // signed in signedIn.hidden = false; signedOff.hidden = true; //display the userDetails //display user name in the username field uname.value = user.displayName; } else { // not signed in signedIn.hidden = true; signedOff.hidden = false; userDetails.innerHTML = ''; window.location.replace("login.html"); } }); //save file to Firestorage then save doc to Firestore //finally set a cooldown on the button auth.onAuthStateChanged(user => { saveBtn.addEventListener("click", (event) => { event.preventDefault(); //check my required files if (requiredFields) { saveBtn.disabled = true; //some logic with my progressbar var metadata = { contentType: 'application/pdf' }; const files = document.getElementById("myfile").files; var storageRef = firebase.storage().ref(); let file = files[0]; // Upload file and metadata to the object 'pdf/filename.pdf' // Listen for state changes, errors, and completion of the upload. uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED, (snapshot) => { // Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded //some logic with the progress to fill my progressbar switch (snapshot.state) { case firebase.storage.TaskState.PAUSED: break; case firebase.storage.TaskState.RUNNING: break; } }, (error) => { switch (error.code) { case 'storage/unauthorized': break; case 'storage/canceled': break; case 'storage/unknown': break; } }, () => { // Upload completed successfully, now we can get the download URL uploadTask.snapshot.ref.getDownloadURL().then((downloadURL) => { const db = firebase.firestore().collection('mycollections'); const userUid = user.uid; const timestamp = new Date().getTime(); try { db.add({ refEvi: downloadURL, userUid: userUid, //all my fields }).then((docRef) => { artRes.innerHTML = "<b>Registro guardado con la referencia: </b>" docRef.id; }); } catch (error) { console.log(error); artRes.innerHTML = "<b>Problema al guardar registro en la base de datos. </b>" error; } finally { //a timer to enable my button a few secs after everything is done } }); } ); } }); }); }
yes i just replaced some code with comments to make this post readable. is there any point in my js where i should unsubscribe from the onAuthStateChanged?
CodePudding user response:
Subscribing to auth state depends on your use case.
- If you want to develop your system in a reactive way, that is, your UI changes itself because it's subscribed to the auth state (no reloads or redirects). Then you should never unsubscribe to auth state.
- The opposite case (you don't want to subscribe) in which you can listen to the auth state change event (response from Firebase after login form submitted) and do the proper changes to the UI. In that case, you should check the auth state every time the page loads. This way, you can reload or redirect since you'll be checking the auth state. You must also know when a user logs out of course.