When signing in to Firebase and updating the user doc using merge: true
, an issue arises when the onAuthStateChanged
listener is also active. This listener detects new logins and then reads the user doc, causing potential conflicts.
Upon further inspection of the functions provided:
export const signInAndUpdateUserObject =
(provider: AuthProvider) => async (): Promise<void> => {
try {
const { user } = (await signInWithPopup(provider)()) ?? {};
if (!user) {
return;
}
await setDoc(doc(getFirestore(), 'users', user.uid), {}, { merge: true }); // Merging an empty object
} catch (err) {
console.error(err);
}
};
export const initAuth = (): void => {
auth.onAuthStateChanged(async (user) => {
if (user) {
const docRef = doc(getFirestore(), 'users', user.uid);
const exists = await getDoc(docRef);
console.log(exists); // THIS WILL SHOW AN EMPTY OBJECT
} else {
return;
}
});
};
(The initAuth function is triggered when the app starts).
In this example, the user doc is updated with an empty object for simplicity. Although merge
prevents changes to the entire document, a peculiar behavior occurs when reading the user doc in onAuthStateChanged
. It seems to only read the merged object, resulting in unexpected outputs. If merge: false
were used instead, the user doc would be correctly retrieved, but this solution is not desirable.
To address this dilemma, introducing a slight delay via setTimeOut within the onAuthStateChanged function resolves the issue:
export const initAuth = (): void => {
auth.onAuthStateChanged(async (user) => {
if (user) {
setTimeout(async () => {
const docRef = doc(getFirestore(), 'users', user.uid);
const exists = await getDoc(docRef);
console.log(exists); // Will show the complete user doc object
}, 3000);
} else {
return;
}
});
};