Home > Software design >  Firebase JS: Why and When to unsubscribe onAuthStateChanged
Firebase JS: Why and When to unsubscribe onAuthStateChanged

Time:02-13

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:

  1. should i unsubscribe() after my sign-in-out onAuthStateChanged ends? if so, where?
  2. is it ok to set all this code inside window.onload ?
  3. 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.
  4. 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.
  • Related