Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Fetch returns undefined the first time a user signs in

I sort of know why it happens, but not sure how to go on about solving it.

I have a React project that uses Cloud Firestore as database, and I have a simple login-page where you can sign in via your Google account. The first time you sign in a new document gets added to the "users" collection in Firebase.

After the document has been created it fetches that user data from Firebase and stores it in Redux.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

const signInWithGoogle = async () => {
    try {
        const res = await signInWithPopup(auth, googleProvider);
        const user = res.user;
        const q = query(collection(db, "users"), where("uid", "==", user.uid));
        const docs = await getDocs(q);
        if(docs.docs.length === 0){
            const firstName = user.displayName.split(' ')[0];
            await addDoc(collection(db, "users"), {
                uid: user.uid,
                name: user.displayName,
                firstName: firstName,
                photoURL: user.photoURL,
                authProvider: "google",
                email: user.email,
            })
            dispatch(getUser(user))
        }
    } catch(err) {
        console.error(err);
        alert(err.message);
    }
}

I also check whenever the user’s auth state changes (here I also do another fetch and store it in Redux).

  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged((user) => {
      setCurrentUser(user);
      setLoading(false);
      if(user){
        dispatch(getUser(user))
      } else {
        console.log("user logout")
      }
    });
    return unsubscribe;
  }, []);

But when a new user signs in the first time, I get an error from the fetch:

export const getUser = createAsyncThunk("profile/getUser", async (user) => {
  try {
    const userQuery = query(
      collection(db, "users"),
      where("uid", "==", user?.uid)
    );
    const doc = await getDocs(userQuery);
    const data = doc.docs[0].data();
    return data;
  } catch (err) {
    console.error(err);
    alert("An error occured while fetching user data");
  }
});

"data" in above block is undefined for a small moment when the user signs in, so the alert in the try/catch block always goes off (it does manage to fetch the data after though).
This error only happens when it’s a new user.

I understand that the fetch occurs before a document has been created in the "users" collection, but I’m not sure how to solve this. I’ve tried to add if/else to certain parts of the code (but just felt like I was grasping for straws).

I’m very much new to Firebase and still learning React, so every bit of help is really appreciated!

>Solution :

Problem is that your signInWithGoogle & useEffect both are running on user’s auth status change. And, when its the new user, signInWithGoogle function makes aysnc call to create default doc, whereas useEffect runs to dispatch action, but at that moment user doesn’t have any linked document. That is why you are getting undefined.

Ideally, you should remove one. You can merge the useEffect into signInWithGoogle to set the user details and dispatch as well.

const signInWithGoogle = async () => {
    try {
        const res = await signInWithPopup(auth, googleProvider);
        const user = res.user;
        const q = query(collection(db, "users"), where("uid", "==", user.uid));
        const docs = await getDocs(q);

        // create `doc` if its the new user
        if(docs.docs.length === 0){
            const firstName = user.displayName.split(' ')[0];
            await addDoc(collection(db, "users"), {
                uid: user.uid,
                name: user.displayName,
                firstName: firstName,
                photoURL: user.photoURL,
                authProvider: "google",
                email: user.email,
            })
        }
        
        // set user info and dispatch
        setCurrentUser(user);
        setLoading(false);
        dispatch(getUser(user))
    } catch(err) {
        console.error(err);
        alert(err.message);
    }
}

Hope that answers your query.

Thanks

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading