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.
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!
CodePudding user response:
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