Firebase Cannot read property 'uid' of null - javascript

For some reason, firebase can't read the property UID. Any help would be greatly appreciated! Should I be changing how I am getting the data from firebase.auth in the first place? The end goal is to update a firestore database file with the ID of the user UID.
// Add additional info to user account
const completeAccountform = document.querySelector('#wf-form-completeAccount');
document.getElementById("completeButton").addEventListener('click', addAccountinfo);
function addAccountinfo() {
// get new account data
const firstName = completeAccountform['firstName'].value;
const lastName = completeAccountform['lastName'].value;
const location = completeAccountform['location'].value;
const db = firebase.firestore();
firebase.auth().currentUser.uid;
db.collection('users').doc(user.uid).set({
email: signupForm['signupEmail'].value,
firstname: completeAccountform['firstName'].value,
lastname: completeAccountform['lastName'].value,
location: completeAccountform['location'].value
}).then(function(docRef) {
modalContainer.style.display = 'none'
console.log("Document updated with ID: ", docRef.id);
})
.catch(function(error) {
console.error("Error updating document: ", error);
});
form.reset()
};

I'd suggest you to follow the recoomendations at the Firebase docs for retrieveng the current user, as it i specified:
The recommended way to get the current user is by setting an observer
on the Auth object:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
By using an observer, you ensure that the Auth object isn't in an
intermediate state—such as initialization—when you get the current
user. When you use signInWithRedirect, the onAuthStateChanged observer
waits until getRedirectResult resolves before triggering.
I'd recommend you to use the observer, because sometimes the auth object has not finished initializing and it'll be null (then you'll have this error). It depends on your Auth flow and what fits your user case better

Related

Uncaught TypeError: Cannot read properties of null (reading 'email')

I am trying to get the email of the currently logged in user which I will then use to get the name data field from the document corresponding to this user. However, I keep getting this error. Any idea on how to fix this issue would be highly appreciated.
Additionally, whenever I make any change in regard to the email issue, I get the following error:
Uncaught TypeError: _firebase__WEBPACK_IMPORTED_MODULE_4__.db.collection is not a function
And when I refresh the page it returns back to the error in the title.
const auth = getAuth();
const user = auth.currentUser;
const userEmail = user.email;
let clubName;
db.collection("users").where("email", "==", userEmail).get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log(doc.id, " => ", doc.data());
let data = doc.data();
clubName = data.name;
});
}).catch(function(error) {
console.log("Error getting documents: ", error);
});
console.log("THE DATA YOU ARE LOOKING FOR: " + clubName);
const q = query(collection(db, "requests"), where("SendTo", "==", clubName));
NOTE: in the code above I am using 2 different firebase databases one called users which has the fields {email, name, password, job} and the other called requests which have the following fields {From, SendTo, type, content, header}
auth.currentUser might be null because the auth object has not finished initializing.
Try using onAuthStateChanged assuming you are using firebase v9.
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
// User is signed in
} else {
// User is signed out
}
});

Firebase Realtime database showing strange behavior

I am using react-native-firebase in an ejected expo app and trying to build a presence detection system in my chat app which will detect that if the message recipient is online and if not when was he/she was last online. The data will be stored as follows in firebase realtime database:
{
lastSeen:[{
[userId]:{
state: boolean
time: serverTimeStamp
}
}]
}
The problem is that firebase console never shows the data and only if recipient is online then app shows this data (even though its not visible in console) but if user is offline then nothing is returned and no error generated. I have set read and write to true in realtimeDB rules. Here is the code I am using:
import database from "#react-native-firebase/database";
export const updateUserLastSeen = (userId) => {
const userStatusDatabaseRef = database().ref("/lastSeen/" + userId);
console.log("updatelast", userId);
userStatusDatabaseRef
.set({
state: true,
time: database.ServerValue.TIMESTAMP,
})
.then(() => console.log("online"))
.catch((e) => console.log(e));
// database()
// .ref(".info/connected")
// .on("value", function (snapshot) {
// if (snapshot.val() == false) {
// return;
// }
userStatusDatabaseRef
.onDisconnect()
.set({
state: false,
time: database.ServerValue.TIMESTAMP,
})
.then(function () {
console.log("disconnect configured");
// userStatusDatabaseRef.set({
// state: true,
// time: database.ServerValue.TIMESTAMP,
// });
});
// });
};
export const checkUserLastSeen = (userId, setUserLastSeen) => {
console.log("check last", userId);
database()
.ref("/lastSeen/" + userId)
.on("value", (snapshot) => {
setUserLastSeen(snapshot.val());
console.log("User data: ", snapshot.val());
});
console.log("after check last");
};
I tried both the code from firebase docs and rnfirebase docs. In above code, none of the "then" or "catch" functions get called in updateUserLastSeen but in checkUserLastSeen "on" is invoked only if bearer of userId is online. Also, I am using realtime db only for this purpose and cloud firestore for other data storing and its working fine.
Any help would be appreciated. Thanks.
If neither then nor catch of a write is called, it typically means that the client is not connected to the server.
I recommend checking to make sure your app has a network connection, and that you've configured the (correct) URL for your database.

Create a firestore doc for each auth user in Nextjs (only using sign in with Google)

I am building a user auth system with Nextjs
I am trying to create a document within firestore for each user in my firebase authentication system. I was easily able to do this in previous projects when creating an account with email and password but with the 'sign in with google' feature I can't seem to figure out how.
I don't want to create a new document every time the user logs in..
My only idea is this:
When user signs in, loop through all firestore documents and see if the users e-mail matches any firestore doc email. If not, create document, else return.
I feel like there is another way though..
Simplest way would be to make a custom hook that can be used anywhere across the application.
First in the _app file inside useeffect hook simply try to get the data from doc if data exist well it means user document is already there and if data does not exists, we need to create a document for that, quite simple. Let's see the code now
Make sure you read comments written inside the code to better understand
In _app.js,
useEffect(async () => {
// now this checks if user is logged in or not
const unsubscribe = auth.onAuthStateChanged(async (userAuth) => {
if (userAuth) {
// if logged in it simply passes the userAuth object to handle user profile
// which is a custom hook to check if document for this user pre-exist or not!
// if there wont be any document it will go and create a document and return
// that document.
// If already there is a document created it will simply return that.
const userRef = await handleUserProfile(userAuth);
userRef.onSnapshot((snapshot) => {
// later you can save currentUsr value in any of the state to use it later
const currentUsr = {
id: snapshot.id,
...snapshot.data(),
};
}
});
}
}
});
return () => unsubscribe();
}, []);
Now the custom hook to check if document is already there or not, here comes the tricky part.
export const handleUserProfile = async (userAuth) => {
// as a second check it check if falsy values are returned
if (!userAuth) return;
const { uid } = userAuth;
// first it tries to get data from that uid
const userRef = firestore.doc(`users/${uid}`);
const snapshot = await userRef.get();
// checks if snapshot exist
if (!snapshot.exists) {
// if snapshot does not exist, it will simply create a document with document
// name as this 'uid'
const { displayName, email } = userAuth;
const timeStamp = new Date();
try {
// making use of same userRef that we created above to create
await userRef.set({
displayName,
email,
createdAt: timeStamp,
});
} catch (error) {}
}
// if snapshot exist it will simply return the userRef which contains the
// document only.
return userRef;
};
Voila! :)
There is no reason why you should not use the onAuthStateChanged event on auth. A write would cost you the same as a read to check if the data is already there. But with a read you would sometimes need also a write. In total only writes every time come less expensive in read/write actions.
Just listen to auth state changes and update your firestore data each time it changes:
firebase.auth().onAuthStateChanged(async (user) => {
if (user) {
await firebase.firestore()
.collection("users")
.doc(user.uid)
.set(data, {merge:true});
// User is signed in.
}
});
Make sure to use set with merge turned on. That will ensure that the data will be created if it doens't exist and update only the field you want to update.
Also make sure to store the data under the user uid. With that you ensure that each user has an unique idenfier. It is a bad practice to store users under the email. One of the reasons for that is that emails could have chars that are not supported as keys so would need to remove those when saving and add them again when reading the keys.
Firestore won't create duplicate docs if created when signing in with Google.. so this works:
const signInWithGoogle = () => {
fire
.auth()
.signInWithPopup(google_provider)
.then((result) => {
/** #type {firebase.auth.OAuthCredential} */
var credential = result.credential;
// This gives you a Google Access Token. You can use it to access the Google API.
var token = credential.accessToken;
// The signed-in user info.
var user = result.user;
// ...
})
.catch((error) => {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
// ...
})
// CREATE USER DATA IN FIRESTORE
.then(async () => {
const data = {
//ADD DATA HERE
};
await fire
.firestore()
.collection("users")
.doc(fire.auth().currentUser.email)
.set(data);
});
};

An error occured: Cannot read property 'stripeId' of null

i changed my stripe and firebase api keys and everything back to test mode for some testing purposes. However, I was unpleasantly presented with this error in my console when trying to redirect to checkout using the firebase stripe api:
An error occured: Cannot read property 'stripeId' of null
however, no where in my code does stripeID exist. interesting. here's the code. oh and keep in mind this only happens in test mode.
var userID = "";
firebase.auth().onAuthStateChanged((user) => {
if(user) {
userID = user.uid
console.log(user.uid)
}
});
export async function createCheckoutSession(activtyStatus){
var price = 'price_1Iav0JKDPaWWeL1yBa9F7Aht'
if (activtyStatus == "canceled") {
// test price
price = 'price_1IelOCKDPaWWeL1ynps36jkc'
}
const checkoutSessionRef = firestore
.collection('customers')
.doc(userID)
.collection('checkout_sessions')
.add({
price: price,
success_url: "https://app.x.com/successPage",
cancel_url: "https://app.x.com/signin",
});
// Wait for the CheckoutSession to get attached by the extension
(await checkoutSessionRef).onSnapshot(function (snap) {
const { error, sessionId } = snap.data();
if (error) {
// Show an error to your customer and
// inspect your Cloud Function logs in the Firebase console.
console.log(`An error occured: ${error.message}`);
}
if (sessionId) {
// We have a session, let's redirect to Checkout
// Init Stripe
const stripe = window.Stripe('pk_test_C');
console.log("going to stripe: ")
stripe.redirectToCheckout({sessionId})
console.log("logged stripe")
}
});
}
export async function goToBilliingPortal(){
var finalRoute = "https://app.x.com/profile"
const functionRef = app
.functions('us-central1')
.httpsCallable('ext-firestore-stripe-subscriptions-createPortalLink');
const {data} = await functionRef({returnUrl : finalRoute});
window.location.assign(data.url);
};
does anyone have any ideas?
Have you checked what the logs of your Stripe dashboard or Firebase Functions say?
I faced similar issue, and looking at the logs it was because of a failure of the createCheckoutSession function. I was having the following message in my Firebase logs:
ext-firestore-stripe-subscriptions-createCheckoutSession
❗️[Error]: Failed to create customer for [XXXXXXXXXXXXXXXXXXXXXX]: This API
call cannot be made with a publishable API key. Please use a secret
API key. You can find a list of your API keys at
https://dashboard.stripe.com/account/apikeys.
I simply had to replace the key in "Stripe API key with restricted access" in Firebase console with a restricted key and it was solved.
I had the same issue, for me it was that my card was declined by google clouds since my card eas expired. I updated my account with another card and no more issues.

How do I add value to a document that is expected to be created? (Firebase + React)

I want to add a field (Username) to a firebase user's document, when i create it.
My logic :
Fill up form with email,pass & username field as well - WORKS.
Take the email & pass and sign up with firebase (createUserWithEmailAndPassword) - WORKS.
Right after the document creation (Hopefully) - fire an HTTP Firebase callable that will update userName field from the form - FAILED.
Error : Unhandled error { Error: 5 NOT_FOUND: No document to update: projects/yeu/databases/(default)/documents/users/DchYhQNMS12VYqzSwtEZ8UXYhcD3
I understand that there is no document (not yet) - so my question is how can i make sure that the document will be ready when it's need to be.
CODE:
ReactJS - SignUp event, (using form & OnClick)
export function SignUp() {
const handleSignupSubmit = (event) => {
event.preventDefault();
const registerForm = document.querySelector(".register");
// firebase
const addUserName = firebase.functions().httpsCallable("addUserName");
// register form
const email = registerForm.email.value;
const password = registerForm.password.value;
const fullName = registerForm.fullName.value;
// firebase sign up
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then((userAuth) => {
console.log("registered", userAuth);
addUserName({
userName : fullName
}).then(((doc)=>{
console.log("UserName Added", doc);
}))
.catch((error) => {
registerForm.reset();
registerForm.querySelector(".error").textContent = error.message;
});
})
.catch((error) => {
registerForm.querySelector(".error").textContent = error.message;
});
};
Firebase Backend - Auth trigger
// auth trigger (new user signup)
exports.newUserSignUp = functions.auth.user().onCreate((user) => {
// for background triggers you must return a value/promise
return admin.firestore().collection("users").doc(user.uid).set({
userName: 'Temp',
email: user.email,
});
});
Firebase Backend - HTTP Callable method
// http callable function (adding a username)
exports.addUserName = functions.https.onCall((data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError("unauthenticated");
}
const userRef = admin.firestore().collection("users").doc(context.auth.uid);
return userRef.get().then((doc) => {
return userRef.update({
userName: data.userName
})
})
});
Your client app should wait for the document to become available before invoking the callable function. The easiest way to do that is to set up a realtime listener for the expected document, then call the function only after the listener gives you a callback indicating that the document is present and has data.
The pattern is described in more detail in this blog post. You should be able to copy what it's doing very closely.

Categories

Resources