Run Function After getAuth() in React Firebase [duplicate] - javascript

This question already has answers here:
why auth.currentUser is null on page refresh in Firebase Authetication
(2 answers)
Can't get currentUser on load
(9 answers)
Closed 23 days ago.
I want to run a function that requires user id. But the function runs before getAuth is finished.
const user = getAuth()
getDoc(doc(db, 'users', user.currentUser.uid))
This is what I want to run and it gives an error saying that user.currentUser.uid is undefined.
What should I do to run the function after getting the uid?
React version: 18.2.0

getAuth() is synchronous, so that is not the problem. The problem is that there is no current user, so your user.currentUser returns undefined and you can't call .uid on that.
If this code runs when the app/page loads, it might be that Firebase is still restoring the user state, which requires it to call to the server (to check a.o. if the account was suspected) and is an asynchronous operation for that reason. If this is the case, you'll want to use an auth state listener as shown in first code snippet in the documentation on getting the current user:
const auth = getAuth()
onAuthStateChanged(auth, (user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
const uid = user.uid;
getDoc(doc(db, 'users', uid))
// ...
} else {
// User is signed out
// ...
}
});

Related

Query collection and get data from other collection using the first one [duplicate]

This question already has answers here:
Using async/await with a forEach loop
(33 answers)
how to do async await on a forEach function [duplicate]
(2 answers)
Closed last month.
I know similar questions have been asked here:
I need to pull data from another Firestore collection based on forEach data within a onSnapshot call
and here:
Using getDoc().then() inside of a loop Firebase
but I really cannot adapt it to my own issue. I actually also consulted ChatGPT, but apparently, it only knows firebase v8.x.x :/
I have a comment collection and I'm querying these for specific content (url). I can get the comments in the first step just fine. Each comment document has a field called "user_id", I want to use this user ID to get information about the user from the collection "userinfo" and push it to the comments array. From what I can understand, the promise I get in my attempt (see code below), indicates that I need to use await, but this is apparently tricky in for loops and inside useEffect.
My attempt is below:
const [allCommments, setAllCommments] = useState([]);
const commentRef = collection(db, "comments");
const q = query(commentRef, where("content", "==", url));
useEffect(() => {
onSnapshot(q, (snapshot) => {
const tempComments = [];
snapshot.forEach((snapDoc) => {
tempComments.push(snapDoc); // this works fine, the next steps doesn't:
const userSnap = getDoc(doc(db, "userinfo", snapDoc.data().user_id));
// tempComments.push(userSnap.data().firstname).push(userSnap.data().lastname) // what I want
// console.log(userSnap.data()) // this gives the error "userSnap.data() is not a function"
// console.log(userSnap); // this only returns a promise
});
setAllCommments(tempComments);
});
}, []);

Firebase - addDoc() returns success even when no document was added

New to Firebase. I have a basic React form that takes a user's email and sends it to Firestore using addDoc().
import { initializeApp } from "firebase/app";
import { getFirestore } from "#firebase/firestore"
const firebaseConfig = {
...
};
// Initialize Firebase
export const app = initializeApp(firebaseConfig);
export const db = getFirestore(app)
const emailsCollectionRef = collection(db, "emails");
const addEmail = async (data: { email: string; }) => {
console.log("Sending data to firebase");
try {
const result = await addDoc(emailsCollectionRef, {email: data!.email});
console.log("Document written with ID: ", result.id);
} catch (e) {
console.error("Error adding document: ", e);
}
}
When I pass the user's input using addDoc() in a try catch I get this in the console
Sending data to firebase
Document written with ID: YFPaCvSjeBssvUAtdqSh
Which is fine, but if I purposefully try to make it fail like for example changing the name of the collection in emailsCollectionRef e.g.
const emailsCollectionRef = collection(db, "NoSuchCollection");
and run the function again I get the same success message.
In the Firebase docs it says that addDoc()
Returns:
Promise<DocumentReference<T>>
A Promise resolved with a DocumentReference pointing to the newly created document after it has been written to the backend (Note that it won't resolve while you're offline).
and under DocumentReference it says
A DocumentReference refers to a document location in a Firestore database and can be used to write, read, or listen to the location. The document at the referenced location may or may not exist.
If I want to check if the write was succesful I have to ignore DocumentReference and immediately after using addDoc() do a separate query to see if that ID actually exists.
My question is, is there a better way to check if the write was actually succesful since DocumentReference obviously cannot be trusted?
Collections are created automatically when you write the first document to is, so quite likely your addDoc call with the non-existing collection actually creates that collection.
If you want to prevent that, you can only allow writes to specific, known collections in your database's security rules, which are enforced on the server. At that point, the promise returns by addDoc will also reject/fail.

How can I make it so that this console.log is executed after the function above it has completed?

I am using firebase and I have run into an issue. What I am trying to do is allow a user to sign up, but to check the account doesn't already exist, I want to retrieve all of the usernames from firestore and then search through them to see if their proposed username already exists. Here is my code to do this:
document.querySelector("#form1").addEventListener("submit", e => {
e.preventDefault();
var users;
firestore.collection("users").get().then(querySnapshot => {
users = querySnapshot.docs.map(doc => doc.data());
console.log(users);
});
console.log(users);
});
So I know that when I try to do console.log(users) for the second time, the function above may not have completed its execution which is why when I log it to the console I see "undefined". How can I change my code so that the second console.log(users) waits for the function above to complete?
I tried looking into this and found some stuff to do with "async" and "wait" but I couldn't figure out how to make these work in my code. Maybe that could be the solution?
I can't warn you enough to not get all users to check if one of them has the same username. I can strongly recommend to query for a single user with the same username:
document.querySelector("#form1").addEventListener("submit", async (e) => {
e.preventDefault();
const theEnteredUsername = ""; // get it from somewhere
// Create a reference to the cities collection
var ref = firestore.collection("users");
// Create a query against the collection.
var query = await ref.where("username", "==", theEnteredUsername).get();
if (query.empty) {
//you can use the username
} else {
//username taken!!!
}
});
Othervise your Firebase bill would be grow extremely with the increase of users in your app.

How to differentiate the fireauth login callback received for Login and signup

I am writing an angular application using firestore.
I am calling Auth::createUserWithEmailAndPassword( to register a user
and also calling Auth::signInWithEmailAndPassword.
In both these cases the user gets logged in and the below subscription gets fired
this.afa.authState.subscribe( authState => {
this.firebaseUser = authState;
if (authState) {
this.onLoginSuccessfulReceivedFromFirebase();
} else {
this.onLogout();
}
this.isLoggedIn = authState != null;
});
Is there a way for me to identify whether the callback got fired at automatic login during createUserWithEmailAndPassword or when specifically calling Auth::signInWithEmailAndPassword within the callback function
You can look authUser metadata created timestamp and login timestamp property to understand when your user logged in to old account or when they created a account and get logged in.
The easiest way to respond to account creation differently is by waiting for the promise of createUserWithEmailAndPassword to resolve.
firebase.auth().createUserWithEmailAndPassword(email, password).then(function(credential) {
console.log(credential.user.uid);
}).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
});

Firebase: Is the error handling in the firebase stripe function example enough?

Firebase provides samples on GitHub for writing cloud functions.
I have a question about the "createStripeCharge" function.
It is possible that the write to the database fails?
If this would be the case, this function charges a customer but no object would be saved to the database. Is this right?
This error handling cannot be enough or do I understand something wrong?
The following line in the code confuses me:
return event.data.adminRef.set(response);
You found the code on GitHub:
https://github.com/firebase/functions-samples/blob/master/stripe/functions/index.js
Or here:
exports.createStripeCharge = functions.database.ref('/stripe_customers/{userId}/charges/{id}').onWrite((event) => {
const val = event.data.val();
// This onWrite will trigger whenever anything is written to the path, so
// noop if the charge was deleted, errored out, or the Stripe API returned a result (id exists)
if (val === null || val.id || val.error) return null;
// Look up the Stripe customer id written in createStripeCustomer
return admin.database().ref(`/stripe_customers/${event.params.userId}/customer_id`).once('value').then((snapshot) => {
return snapshot.val();
}).then((customer) => {
// Create a charge using the pushId as the idempotency key, protecting against double charges
const amount = val.amount;
const idempotency_key = event.params.id;
let charge = {amount, currency, customer};
if (val.source !== null) charge.source = val.source;
return stripe.charges.create(charge, {idempotency_key});
}).then((response) => {
// If the result is successful, write it back to the database
return event.data.adminRef.set(response);
}).catch((error) => {
// We want to capture errors and render them in a user-friendly way, while
// still logging an exception with Stackdriver
return event.data.adminRef.child('error').set(userFacingMessage(error));
}).then(() => {
return reportError(error, {user: event.params.userId});
});
});
The only way a database write can fail is if it violates a security rule or if the function times out and gets cleaned up before it finishes. When using the admin SDK, security rules don't apply, so that can't be the cause. I suppose it's possible for the write to time out, so if you are very concerned about that, you should increase the timeout of the function. The chance of a timeout occurring is extremely low. If that happens, you should be able to manually resolve the concern of an end user without wasting a lot of time.
The onWrite():
triggers when data is created, updated, or deleted in the Realtime Database.
exports.createStripeCharge = functions.database.ref('/stripe_customers/{userId}/charges/{id}').onWrite((event) => {
Therefore if the write to the database in the above location fails then onWrite() will not trigger.
Also if you want it to trigger when adding data only then you can use onCreate():
It triggers when new data is created in the Realtime Database.
more info here:
https://firebase.google.com/docs/functions/database-events

Categories