Home > Enterprise >  How can I minimize firestore reads in the "setUsername" scenario?
How can I minimize firestore reads in the "setUsername" scenario?

Time:04-01

the code I have now created is working, but I think it is somehow suboptimal, as it does to many database reads As far as I understand it, the "onAuthStateChange" can be understood like an useEffect hook, which gets called whenever the user authentication state changes (login, logout). Whenever this happens, the database should be checked for the username which has been chosen by the user. But if I take a look at the console the docSnap is logged a fair amount of times which to me indicates that the function gets called more often than just when the user logs in / logs out.

enter image description here

Context Component

import { createContext } from "react/cjs/react.production.min";
import { onAuthStateChanged } from "firebase/auth";
import { doc, getDoc } from "firebase/firestore";
import { auth,db } from "./firebase";
import { useState, useEffect } from "react";

export const authContext = createContext({
  user: "null",
  username: "null",
});

export default function AuthenticationContext(props) {
  const [googleUser, setGoogleUser] = useState(null);
  const [username, setUsername] = useState(null);
  const [userID, setUserID] = useState(null);

  onAuthStateChanged(auth, (user) => {
    if (user) {
      setGoogleUser(user.displayName);
      setUserID(user.uid);
      getUsername();
    } else {
      setGoogleUser(null);
    }
  });


    const getUsername = async () => {
      const docRef = doc(db, `users/${userID}`);
      const docSnap = await getDoc(docRef);
      if(docSnap.exists()){
        setUsername(docSnap.data().username);
      }
      else{
        setUsername(null);
      }
    };
  

  return (
    <authContext.Provider value={{ user: googleUser, username: username }}>
      {props.children}
    </authContext.Provider>
  );
}

What is more, is that when I login with google and submit a username, the components do not get reevaluated - so a refresh is necessary in order for all the changes to take effect, this has something to do with me not updating state in the submitHandler of the login page. If you have some ideas on how I can do this more professionally please let me hear them. Thank you in advance!

Submit Handler on Login Page

const submitHandler = async (event) => {
    event.preventDefault();
    console.log(auth.lastNotifiedUid);
    await setDoc(doc(db, "users", auth.lastNotifiedUid), {
      displayName: user,
      username: username,
    });
    await setDoc(doc(db, "usernames", username), { uid: auth.lastNotifiedUid });
  };

CodePudding user response:

As pointed out in the comments by Dharmaraj, you set multiple subscriptions to the authentication state. This is because you call onAuthStateChanged in the body of your component, so it is executed on every render.

To avoid this, you should wrap the function in useEffect so that you only subscribe on component mounting, and unsubscribe on unmounting:

export default function AuthenticationContext(props) {
  /* ... */
  React.useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => { /* ... */ });
    return unsubscribe;
  }, []);
  /* ... */
}
  • Related